cat a.txt | xargs -I % echo %
在上面的示例中,xargs将echo %
作为命令参数。但在某些情况下,我需要多个命令来处理参数而不是一个。例如:
cat a.txt | xargs -I % {command1; command2; ... }
但是xargs不接受这种形式。我知道的一个解决方案是我可以定义一个函数来包装命令,但它不是一个管道,我不喜欢它。还有其他解决方案吗?
cat a.txt | xargs -I % sh -c 'command1; command2; ...'
请注意,这是一个Useless Use Of cat。我把它写成:
< a.txt xargs -I % sh -c 'command1; command2; ...'
(是的,重定向可以在命令的开头。)
据推测,command1
和/或command2
将包含一个或多个%
字符;否则-I %
的xargs
选项就没有多大意义了。
我目前的BKM是这样的
... | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");'
不幸的是,这使用了perl,它比bash更不可能安装;但它处理接受答案的更多输入。 (我欢迎一个不依赖于perl的无处不在的版本。)
@ KeithThompson的建议
... | xargs -I % sh -c 'command1; command2; ...'
很棒 - 除非你的输入中有shell注释字符#,在这种情况下,第一个命令的一部分和第二个命令的所有部分都将被截断。
如果输入来自文件系统列表(例如ls或find),并且编辑器在其名称中创建带有#的临时文件,则哈希#可能非常常见。
问题示例:
$ bash 1366 $> /bin/ls | cat
#Makefile#
#README#
Makefile
README
糟糕,这是问题所在:
$ bash 1367 $> ls | xargs -n1 -I % sh -i -c 'echo 1 %; echo 2 %'
1
1
1
1 Makefile
2 Makefile
1 README
2 README
啊,那更好:
$ bash 1368 $> ls | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");'
1 #Makefile#
2 #Makefile#
1 #README#
2 #README#
1 Makefile
2 Makefile
1 README
2 README
$ bash 1369 $>
使用GNU Parallel,您可以:
cat a.txt | parallel 'command1 {}; command2 {}; ...; '
观看介绍视频以了解更多信息:https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
这只是没有xargs和cat的另一种方法:
while read stuff; do
command1 "$stuff"
command2 "$stuff"
...
done < a.txt
您可以使用
cat file.txt | xargs -i sh -c 'command {} | command2 {} && command3 {}'
{} =文本文件中每一行的变量
我做的一件事是添加.bashrc / .profile这个函数:
function each() {
while read line; do
for f in "$@"; do
$f $line
done
done
}
然后你可以做的事情
... | each command1 command2 "command3 has spaces"
它比xargs或-exec更简洁。如果您还需要该行为,您还可以修改函数以将读取的值插入命令中的任意位置。
我更喜欢允许干运行模式的风格(没有| sh
):
cat a.txt | xargs -I % echo "command1; command2; ... " | sh
也适用于管道:
cat a.txt | xargs -I % echo "echo % | cat " | sh
派对有点晚了。
我使用下面的格式在迁移之前用数千个小文件压缩我的目录。如果您在命令中不需要单引号,它应该可以工作。
经过一些修改,我确信它对某人有用。在Cygwin
(babun)测试
find . -maxdepth 1 ! -path . -type d -print0 | xargs -0 -I @@ bash -c '{ tar caf "@@.tar.lzop" "@@" && echo Completed compressing directory "@@" ; }'
find .
在这里找到
-maxdepth 1
不要进入子目录
! -path .
排除。 /当前目录路径
-type d
只匹配目录
-print0
以null字节\ 0分隔输出
| xargs
管道到xargs
-0
输入为空分隔字节
-I @@
占位符是@@。用输入替换@@。
bash -c '...'
Run Bash命令
{...}
命令分组
&&
仅在上一个命令成功退出时执行下一个命令(退出0)
最终的;
很重要,否则会失败。
输出:
Completed compressing directory ./Directory1 with meta characters in it
Completed compressing directory ./Directory2 with meta characters in it
Completed compressing directory ./Directory3 with meta characters in it
2018年7月更新:
如果你喜欢黑客和玩耍,这里有一些有趣的东西:
echo "a b c" > a.txt
echo "123" >> a.txt
echo "###this is a comment" >> a.txt
cat a.txt
myCommandWithDifferentQuotes=$(cat <<'EOF'
echo "command 1: $@"; echo 'will you do the fandango?'; echo "command 2: $@"; echo
EOF
)
< a.txt xargs -I @@ bash -c "$myCommandWithDifferentQuotes" -- @@
输出:
command 1: a b c
will you do the fandango?
command 2: a b c
command 1: 123
will you do the fandango?
command 2: 123
command 1: ###this is a comment
will you do the fandango?
command 2: ###this is a comment
说明:
- 创建单个线性脚本并将其存储在变量中
- xargs
读取a.txt
并将其作为bash
脚本执行
- @@
确保每次通过整条线路
- 在@@
之后放置--
确保@@
被作为bash
命令的位置参数输入,而不是bash
开始OPTION
,即像-c
本身这意味着run command
--
是神奇的,它适用于许多其他的东西,即ssh
,甚至kubectl
对我有用的另一个可能的解决方案是 -
cat a.txt | xargs bash -c 'command1 $@; command2 $@' bash
注意最后的'bash' - 我假设它作为argv [0]传递给bash。如果没有此语法,则每个命令的第一个参数都将丢失。它可能是任何一个词。
例:
cat a.txt | xargs -n 5 bash -c 'echo -n `date +%Y%m%d-%H%M%S:` ; echo " data: " $@; echo "data again: " $@' bash
这似乎是最安全的版本。
tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1 "$@"; command2 "$@";' ''
(-0
可以删除,tr
替换为重定向(或文件可以替换为空分隔文件)。它主要在那里因为我主要使用xargs
与find
与-print0
输出)(这也可能是相关的没有xargs
扩展的-0
版本)
它是安全的,因为args会在执行时将参数作为数组传递给shell。当使用bash
获得所有shell时,shell(至少["$@"][1]
)会将它们作为未更改的数组传递给其他进程
如果使用...| xargs -r0 -I{} bash -c 'f="{}"; command "$f";' ''
,如果字符串包含双引号,则赋值将失败。对于使用-i
或-I
的每个变体都是如此。 (由于它被替换为字符串,您始终可以通过在输入数据中插入意外字符(如引号,反引号或美元符号)来注入命令)
如果命令一次只能获取一个参数:
tr '[\n]' '[\0]' < a.txt | xargs -r0 -n1 /bin/bash -c 'command1 "$@"; command2 "$@";' ''
或者使用更少的进程:
tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'for f in "$@"; do command1 "$f"; command2 "$f"; done;' ''
如果您有GNU xargs
或其他具有-P
扩展名并且您希望并行运行32个进程,每个进程的每个命令不超过10个:
tr '[\n]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1 "$@"; command2 "$@";' ''
这应该对输入中的任何特殊字符都很健壮。 (如果输入为空分隔。)如果某些行包含换行符,则tr
版本将获得一些无效输入,但对于换行符分隔文件,这是不可避免的。
bash -c
的空白第一个参数是由于:(来自bash
man page)(谢谢@clacke)
-c If the -c option is present, then commands are read from the first non-option argument com‐
mand_string. If there are arguments after the command_string, the first argument is assigned to $0
and any remaining arguments are assigned to the positional parameters. The assignment to $0 sets
the name of the shell, which is used in warning and error messages.