如何将shell命令应用到命令输出的每一行?

假设我有一个命令的输出(比如ls -1):

a
b
c
d
e
...

我想依次对每个对象应用命令(比如echo)。如。

echo a
echo b
echo c
echo d
echo e
...

在bash中最简单的方法是什么?

218565 次浏览

你可以在每一行上使用一个基本的前置操作:

ls -1 | while read line ; do echo $line ; done

或者你可以将输出管道到sed进行更复杂的操作:

ls -1 | sed 's/^\(.*\)$/echo \1/'

你可以使用for循环:

for file in * ; do
echo "$file"
done

注意,如果所讨论的命令接受多个参数,那么使用xargs几乎总是更有效,因为它只需要生成一次而不是多次。

xargs可能是最简单的方法。在你的情况下:

ls -1 | xargs -L1 echo

-L标志确保输入被正确读取。从xargs的手册页:

-L number
Call utility for every number non-empty lines read.
A line ending with a space continues to the next non-empty line. [...]
for s in `cmd`; do echo $s; done

如果cmd有一个大的输出:

cmd | xargs -L1 echo

你实际上可以使用sed来做它,前提是它是GNU sed。

... | sed 's/match/command \0/e'

工作原理:

  1. 用命令匹配替换匹配
  2. 在替换执行命令
  3. 用命令输出替换被替换的行。

对我来说更好的结果是:

ls -1 | xargs -L1 -d "\n" CMD

Xargs用反斜杠和引号失败。它需要像这样

ls -1 |tr \\n \\0 |xargs -0 -iTHIS echo "THIS is a file."

Xargs -0选项:

-0, --null
Input  items are terminated by a null character instead of by whitespace, and the quotes and backslash are
not special (every character is taken literally).  Disables the end of file string, which is treated  like
any  other argument.  Useful when input items might contain white space, quote marks, or backslashes.  The
GNU find -print0 option produces input suitable for this mode.

ls -1以换行符结束项,因此tr将它们转换为空字符。

这种方法比手动迭代for ...慢了大约50倍(参见迈克尔·亚伦·萨芬s的答案)(3.55秒vs. 0.066秒)。但对于其他输入命令,如locate, find,从文件中读取(tr \\n \\0 <file)或类似命令,你必须像这样使用xargs

例如,我喜欢使用gawk在一个列表上运行多个命令

ls -l | gawk '{system("/path/to/cmd.sh "$1)}'

然而,可转义字符的转义可能会有点麻烦。

一个解决方案,工作的文件名中有空格,是:

ls -1 | xargs -I %s echo %s

下面是等价的,但是前体和你实际想做的事情之间有一个更清晰的界限:

ls -1 | xargs -I %s -- echo %s

其中echo是你想要运行的任何东西,随后的%s是文件名。

多亏了克里斯Jester-Young重复的问题上的回答