如何在给定输入列表的每个输入上运行命令

如何在给定输入列表的每个输入上运行命令

我有一堆文件夹,我想 cd 进入每个文件夹并运行命令。我觉得应该是这样的:

(以下是我想做的一个人为的简化示例)。

ls | xargs echo 'Looking at {}' && cd {} && /bin/do_stuff

但这是行不通的。我该怎么做呢?

答案1

它不起作用,因为xargs直接执行它的命令。它不接受一行shell代码并将命令传递给shell来执行。事实上,所有这些&&s 都由 shell 解释 ls甚至xargs跑步。另外,{}是一个可以理解的结构find,而不是xargs

(如果您愿意,您可以使用-Iwithxargs来模仿 的该功能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 的工作目录。)

相关内容