首页 文章

通过xargs调用多个命令

提问于
浏览
235
cat a.txt | xargs -I % echo %

在上面的示例中,xargs将 echo % 作为命令参数 . 但在某些情况下,我需要处理多个命令而不是一个,例如:

cat a.txt | xargs -I % {command1; command2; ... }

但是xargs不接受这种形式 . 我知道的一个解决方案是我可以定义一个函数来包装命令,但它不是管道,我不喜欢它 . 还有其他解决方案吗?

10 回答

  • 1
    cat a.txt | xargs -I % sh -c 'command1; command2; ...'
    

    请注意,这是Useless Use Of cat . 我把它写成:

    < a.txt xargs -I % sh -c 'command1; command2; ...'
    

    (是的,重定向可以在命令的开头 . )

    据推测 command1 和/或 command2 将包含一个或多个 % 个字符;否则 -I %-I % 选项就没有多大意义了 .

  • 14

    使用GNU Parallel,您可以:

    cat a.txt | parallel 'command1 {}; command2 {}; ...; '
    

    观看介绍视频以了解更多信息:https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

  • 0

    这只是没有xargs和cat的另一种方法:

    while read stuff; do
      command1 "$stuff"
      command2 "$stuff"
      ...
    done < a.txt
    
  • 26

    您可以使用

    cat file.txt | xargs -i  sh -c 'command {} | command2 {} && command3 {}'
    

    {} =文本文件中每一行的变量

  • 5

    我做的一件事是添加.bashrc / .profile这个函数:

    function each() {
        while read line; do
            for f in "$@"; do
                $f $line
            done
        done
    }
    

    然后你可以做的事情

    ... | each command1 command2 "command3 has spaces"
    

    它比xargs或-exec更简洁 . 如果您还需要该行为,您还可以修改函数以将读取的值插入命令中的任意位置 .

  • 23

    我更喜欢允许干运行模式的风格(没有 | sh ):

    cat a.txt | xargs -I % echo "command1; command2; ... " | sh
    

    也适用于管道:

    cat a.txt | xargs -I % echo "echo % | cat " | sh
    
  • 15

    派对有点晚了 .

    我使用下面的格式在迁移之前用数千个小文件压缩我的目录 . 如果您在命令中不需要单引号,它应该可以工作 .

    经过一些修改,我确信它对某人有用 . 测试 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 '...' 运行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 July Update:

    如果你喜欢黑客并玩耍,这里有一些有趣的东西:

    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 start OPTION ,即 -c 本身,这意味着 run command

    -- 是神奇的,它适用于许多其他的东西,即 ssh ,甚至 kubectl

  • 6

    对我有用的另一个可能的解决方案是 -

    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
    
  • 7

    我目前的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 $>
    
  • 349

    这似乎是最安全的版本 .

    tr '[\n]' '[\0]' < a.txt | xargs -r0 /bin/bash -c 'command1 "$@"; command2 "$@";'
    

    -0 可以删除, tr 替换为重定向(或者文件可以替换为空分隔文件) . 它主要在那里,因为我主要使用 xargsfind-print0 输出)(这也可能与没有 -0 扩展名的 xargs 版本)

    它是安全的,因为args会将参数传递给shell作为执行它的数组 . 当使用 ["$@"][1] 获取所有shell时,shell(至少 bash )会将它们作为未更改的数组传递给其他进程

    如果使用 ...| 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 版本将获得一些无效输入,但对于换行符分隔文件,这是不可避免的 .

相关问题