我有一堆文件夹,我想 cd 进入每个文件夹并运行命令。我觉得应该是这样的:
(以下是我想做的一个人为的简化示例)。
ls | xargs echo 'Looking at {}' && cd {} && /bin/do_stuff
但这是行不通的。我该怎么做呢?
答案1
它不起作用,因为xargs
直接执行它的命令。它不接受一行shell代码并将命令传递给shell来执行。事实上,所有这些&&
s 都由 shell 解释前 ls
甚至xargs
跑步。另外,{}
是一个可以理解的结构find
,而不是xargs
。
(如果您愿意,您可以使用-I
withxargs
来模仿 的该功能find
。)
最简单的方法xargs
是将命令放入 shell 脚本中:
#!/bin/sh
echo "Looking at $1"
cd $1
/bin/do_stuff
(我已经有效地将您替换&&
为,;
因为它看起来像echo
并且cd
不会失败,只要您在仅包含其他子目录的目录中运行它。如果您需要“和”行为,则对脚本所需的修复是显而易见的。)
然后,通过 运行该 shell 脚本xargs
。如果你调用它dostuff.sh
:
$ ls | xargs ./dostuff.sh
dostuff.sh
需要标记为可执行,就像chmod +x dostuff.sh
.
可能有一种方法可以用 来将所有这些写在一行上sh -c
,但是如果您不记得做这样的事情的魔力,那主要只是出于好奇,不是吗?
答案2
xargs
是正确的工具,但很难使用。
xargs
不运行 shell,除非你告诉它。它运行第一个非选项参数中给出的命令,并向其传递命令行上的其他参数,然后是从标准输入读取的参数。例如
echo hello world | xargs echo 'Looking at'
运行echo 'Looking at' hello world
。要每个输入行运行一次命令,请使用xargs -L1
:
(echo hello; echo world) | xargs -L1 echo 'Looking at'
由于您想要xargs
运行 shell 命令,因此需要使其显式调用 shell。
(echo hello; echo world) | xargs sh -c 'echo "Looking at $0" && cd "$0" && /bin/do_stuff'
(或者tcsh -c
如果你真的想要的话,但来吧,这是 21 世纪。)
您可以使用在命令内部-I {}
进行插值,但请注意,这仅在输入不包含任何 shell 特殊字符时才有效。{}
将输入作为参数传递给命令($0
上面)的优点是不对输入做出任何假设(除了按照语法进行行分隔且不包含尾随空格之外xargs -L
)。
在 Bourne/POSIX 风格的 shell 中,另一种使用更多行但更清晰的方法是在 shell 内置函数周围使用循环read
:
{ echo hello; echo world; } |
while IFS= read -r line; do
echo "Looking at $line"
(cd "$line" && /bin/do_stuff)
done
(请注意,我cd
在子 shell 中执行,因为整个循环是在同一个 shell 中执行的,并且我不想更改该 shell 的工作目录。)