Calling multiple commands with xargs
1 | cat a.txt | xargs -I % echo % |
在上面的示例中,xargs将
1 | cat a.txt | xargs -I % {command1; command2; ... } |
但是xargs不接受这种形式。 我知道的一个解决方案是,我可以定义一个包装命令的函数,但这不是管道,我不喜欢它。 还有其他解决方案吗?
1 2 | cat a.txt | xargs -d $' ' sh -c 'for arg do command1"$arg"; command2"$arg"; ...; done' _ |
...或者没有对猫的无用使用:
1 2 | <a.txt xargs -d $' ' sh -c 'for arg do command1"$arg"; command2"$arg"; ...; done' _ |
要解释一些更好的观点:
-
出于安全原因,使用
"$arg" 代替% (并且xargs 命令行中没有-I )是:在sh 的命令行参数列表中传递数据,而不是将其替换为代码可以防止数据可能包含的内容(例如$(rm -rf ~) ,以一个特别的恶意示例)作为代码执行。 -
类似地,
-d $' 的使用是GNU扩展,它导致
'xargs 将输入文件的每一行都视为一个单独的数据项。要防止xargs尝试对读取的流进行类似于shell的解析(但不完全与shell兼容),必须使用this或-0 (期望使用NUL而不是换行符)。 (如果没有GNU xargs,则可以使用tr '
' '\0' to get line-oriented reading without-d )。 -
_ 是$0 的占位符,因此xargs 添加的其他数据值将变为$1 并向前,这恰好是for 循环迭代的默认值集。
使用GNU Parallel,您可以:
1 | cat a.txt | parallel 'command1 {}; command2 {}; ...; ' |
观看介绍性视频以了解更多信息:https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
这只是没有xargs或cat的另一种方法:
1 2 3 4 5 | while read stuff; do command1"$stuff" command2"$stuff" ... done < a.txt |
您可以使用
1 | cat file.txt | xargs -i sh -c 'command {} | command2 {} && command3 {}' |
{} =文本文件中每一行的变量
我要做的一件事是将.bashrc / .profile添加到此函数:
1 2 3 4 5 6 7 | function each() { while read line; do for f in"$@"; do $f $line done done } |
然后你可以做
1 | ... | each command1 command2"command3 has spaces" |
它不如xargs或-exec冗长。您也可以修改该函数,以将读取值插入命令中的任意位置(如果需要),从而将其插入每个命令。
我更喜欢允许空运行模式(没有
1 | cat a.txt | xargs -I % echo"command1; command2; ..." | sh |
也适用于管道:
1 | cat a.txt | xargs -I % echo"echo % | cat" | sh |
晚会晚了。
我使用以下格式在迁移之前用数千个小文件压缩目录。如果您在命令中不需要单引号,则应该可以使用。
经过一些修改,我相信它将对某人有用。在
1 | find . -maxdepth 1 ! -path . -type d -print0 | xargs -0 -I @@ bash -c '{ tar caf"@@.tar.lzop""@@" && echo Completed compressing directory"@@" ; }' |
最终
输出:
1 2 3 | 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月更新:
如果您喜欢黑客和游戏,这里有一些有趣的事情:
1 2 3 4 5 6 7 8 9 | 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" -- @@ |
输出:
1 2 3 4 5 6 7 8 9 10 11 | 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 |
说明:
-创建一个线性脚本并将其存储在变量中
-
-
-将
对我有用的另一种可能的解决方案是-
1 | cat a.txt | xargs bash -c 'command1 $@; command2 $@' bash |
注意最后的" bash"-我假设它以argv [0]的形式传递给bash。如果没有使用此语法,则会丢失每个命令的第一个参数。可能是任何单词。
例:
1 | cat a.txt | xargs -n 5 bash -c 'echo -n `date +%Y%m%d-%H%M%S:` ; echo" data:" $@; echo"data again:" $@' bash |
这似乎是最安全的版本。
1 2 | tr '[ ]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1"$@"; command2"$@";' '' |
(可以删除
这是安全的,因为args在执行时会将参数作为数组传递给Shell。当使用
如果使用
如果这些命令一次只能使用一个参数:
1 2 | tr '[ ]' '[\0]' < a.txt | xargs -r0 -n1 /bin/bash -c 'command1"$@"; command2"$@";' '' |
或使用较少的流程:
1 2 | tr '[ ]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'for f in"$@"; do command1"$f"; command2"$f"; done;' '' |
如果您具有GNU
1 2 | tr '[ ]' '[\0]' < a.txt | xargs -r0 -n10 -P32 /bin/bash -c 'command1"$@"; command2"$@";' '' |
对于输入中的任何特殊字符,这应该是可靠的。 (如果输入为null分隔。)如果某些行包含换行符,则
1 2 3 4 | -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. |
我目前的BKM是
1 | ... | xargs -n1 -I % perl -e 'system("echo 1 %"); system("echo 2 %");' |
不幸的是,它使用了perl,它比bash不太可能被安装。但它处理的输入要多于可接受的答案。 (我欢迎不依赖perl的无处不在的版本。)
@KeithThompson的建议
1 | ... | xargs -I % sh -c 'command1; command2; ...' |
太好了-除非您在输入中输入了shell注释字符#,否则第一个命令的一部分和第二个命令的所有部分都会被截断。
如果输入源于文件系统列表(例如ls或find),并且您的编辑器创建名称为#的临时文件,则哈希号#很常见。
问题示例:
1 2 3 4 5 | $ bash 1366 $> /bin/ls | cat #Makefile# #README# Makefile README |
糟糕,这是问题所在:
1 2 3 4 5 6 7 8 | $ bash 1367 $> ls | xargs -n1 -I % sh -i -c 'echo 1 %; echo 2 %' 1 1 1 1 Makefile 2 Makefile 1 README 2 README |
嗯,这样更好:
1 2 3 4 5 6 7 8 9 10 | $ 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 $> |