(请参阅问题底部的更新)。
这是一个后续问题“使用 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
然后用旧的内容加上新的、精简的项目来重建。