在 bash 中循环目录条目并保存到数组

在 bash 中循环目录条目并保存到数组

(请参阅问题底部的更新)。

这是一个后续问题“使用 find 制作目录副本”

这个问题涉及操作一堆目录,这太复杂了,无法用单个命令处理,所以我决定采用一种将目录列表保存到 bash 数组的方法,尽管对可移植性有所保留。 POSIX shell 标准(据我所知是 Unix shell 标准)似乎没有数组。

我正在使用的 makefile 如下所示。除了最后一步之外,这都有效。我正在尝试做的事情的总结如下。

正如前面的问题中所讨论的,我想循环遍历目录,并将其包含该文件(但也可能包含其他文件)x86-headers的顶级子目录的列表收集到bash数组中。C/populate.sh例如,在我的测试设置中,只有一个目录x86-headers包含该文件libc/C/populate.sh,即 libc.

然后我对这些子目录执行一些操作。其中最重要的是我为每个目录制作了一个临时副本,看起来像libc.tmp_g4zlb.即 dirname 后跟“tmp_”,后跟 5 位随机字符串。

那么,一些问题:

1)正如前面的问题中所讨论的,我正在循环目录“x86-headers”。这里我还是用find。 @Gilles 指出他的回答这不是理想的。他可能是对的。此处的 find 问题:

a) 返回的值类似于./libc。我不想要领先的 ./

b) 我正在使用的 find 命令列出了所有目录。我只想考虑那些包含相对路径为 C/populate.sh.

吉尔斯使用的方法可能更好,但我不明白。请参阅gilles下面的目标。我想获取目录列表并将它们保存到数组中。

2)我遇到问题的是最后一步,即

echo "(progn (require 'parse-ffi) (ccl::parse-standard-ffi-files :$$i'.tmp_'$(RND)))" | \
/usr/lib/ccl-bootstrap/lx86cl -I /usr/lib/ccl-bootstrap/lx86cl.image; done \

相关的一点是我试图将临时值传递 libc.tmp_g4zlb给 ccl,它是一个 Common Lisp 编译器。如果没有替换,它看起来像

echo "(progn (require 'parse-ffi) (ccl::parse-standard-ffi-files :libc.tmp_g4zlb))" | \
/usr/lib/ccl-bootstrap/lx86cl -I /usr/lib/ccl-bootstrap/lx86cl.image; done \

这有效。上面使用的版本$$i没有。问题似乎是主导./。没有它,它应该可以工作。作为记录,我得到的错误是

? > Error: Too many arguments in call to #<Compiled-function CCL::PARSE-STANDARD-FFI-FILES #x488B8406>:
>        3 arguments provided, at most 2 accepted. 
> While executing: CCL::PARSE-STANDARD-FFI-FILES, in process listener(1).

然而,这不是我在传递./libc.tmp_g4zlb给编译器时遇到的错误。该错误看起来像

> Error: Filename "/home/faheem/test/foo/x86-headers/.\\/libc.tmp_g4zlb/C/**/" contains illegal character #\/
> While executing: CCL::%SPLIT-DIR, in process listener(1).

所以可能还有其他事情发生。

3) 整体方法看起来合理吗?请随意提出可能的改进建议,即使它涉及完全不同的策略。

#!/usr/bin/make -f
# -*- makefile -*-

export SHELL=/bin/bash
export RND:=$(shell tr -cd a-z0-9 < /dev/urandom | head -c5)
export CCL_DEFAULT_DIRECTORY=$(CURDIR)

clean:
    rm -rf x86-headers/libc.tmp*

foo: clean
    PATH=$$PATH:$(CURDIR)/ffigen4/bin; \
    echo $$PATH; \
    export CURDIR=$(CURDIR); \
    echo $$CURDIR; \
    array=( $$(cd x86-headers && find . -mindepth 1 -maxdepth 1 -type d) ); \
    cd x86-headers && \
    for i in "$${array[@]}"; do \
    echo $$i; done; \
    for i in "$${array[@]}"; do \
    mkdir -p $$i."tmp_"$(RND)/C; done; \
    for i in "$${array[@]}"; do \
    cp -p $$i/C/populate.sh $$i".tmp_"$(RND)/C; done; \
    for i in "$${array[@]}"; do \
    cd $$i".tmp_"$(RND)/C; ./populate.sh; done; \
    for i in "$${array[@]}"; do \
    echo $$i'.tmp_'$(RND); done; \
    for i in "$${array[@]}"; do \
    echo "(progn (require 'parse-ffi) (ccl::parse-standard-ffi-files :$$i'.tmp_'$(RND)))" | \
    /usr/lib/ccl-bootstrap/lx86cl -I /usr/lib/ccl-bootstrap/lx86cl.image; done; \

gilles:
    cd x86-headers;
    for x in */C/populate.sh; do \
    echo -- "$${x%%/*}$$suffix"; done; \

更新:这个问题(或多个问题)可能会迷失在所有细节中。那么,让我尝试简化事情。在他的回答,吉尔斯写道

for x in */C/populate.sh; do
  mkdir -- "${x%%/*}$suffix"
  mkdir -- "${x%%/*}$suffix/C"
  cp -p -- "$x" "./${x%%/*}$suffix/C"
done

当我评论他的问题时,x这里匹配了形式的模式*/C/populate.sh。另外,${x%%/*}匹配字符串的第一部分,即顶级目录名称。现在,像

for x in */C/populate.sh; do                                                                                                                       
    myarr[] = "${x%%/*}"                                                                                                                    
done

将创建一个包含顶级目录列表的数组,这就是我想要的。但是,我不知道该使用什么语法。我需要使用一个在循环上运行的计数器,就像在 LHS 上i=0, 1,...建立索引一样。myarr如果我有一段这样的工作代码,它将在某种程度上解决我的问题。

答案1

如果你想在循环中将东西追加到数组中,你可以使用类似的东西:

for x in */C/populate.sh; do
  myarr=(${myarr[@]} "${x%%/*}")
done

看狂欢数组您可以用它们做很多事情的文档。

或者,您可以使用+=附加到数组(请参阅这里) 像这样:

  myarr+=("${x%%/*}")

一些评论:

当我评论他的问题时, x 这里匹配 形式的模式*/C/populate.sh

我不会这么解释的。*/foo/bar是一个全局模式。它由 shell 扩展为与此模式匹配的所有文件/目录的列表。 (尝试一下echo */C/populate.sh,您将看到打印的所有匹配项。)
循环使用作为循环变量for来迭代这组匹配项。$x

另外,${x%%/*}匹配字符串的第一部分,即顶级目录名称。

${x%%/*}不“匹配”任何东西。它是一个在$x.从文档,从 的末尾${var%%string}删除最长的匹配项。在这种情况下,它会删除从第一个开始的所有内容并“返回”(扩展到)该内容。string$var/

因此,将上述三行代码分解后,会发生以下情况:

  • shell 生成与 glob 匹配的项目(文件或目录)列表*/C/populate.sh
  • 对于其中的每一个,循环体都是在$x设置为该项目的情况下执行的
  • ${myarr[@]}扩展到数组中每个项目的列表
  • ${x%%/*}$x从一/开始扩展到减去所有内容
  • myarr然后用旧的内容加上新的、精简的项目来重建。

相关内容