【问题标题】:Calling shell functions with xargs使用 xargs 调用 shell 函数
【发布时间】:2012-06-15 17:37:20
【问题描述】:

我正在尝试使用 xargs 来并行调用更复杂的函数。

#!/bin/bash
echo_var(){
    echo $1
    return 0
}
seq -f "n%04g" 1 100 |xargs -n 1 -P 10 -i echo_var {} 
exit 0

这会返回错误

xargs: echo_var: No such file or directory

欢迎任何关于如何使用 xargs 来完成此任务的想法或任何其他解决方案。

【问题讨论】:

  • 危险,user1148366,危险!不要使用 bash 进行并行编程——你会遇到很多问题。使用 C/C++ 和 pthread,或 Java 线程,或任何让您对自己正在做的事情进行长时间思考的东西,因为并行编程需要大量思考才能做到正确。
  • @DavidSouther 如果任务是独立的,例如将所有这些图片文件转换为 png,那么不用担心。当你有同步(而不是等待所有完成)和通信时,它就会变得混乱。
  • @DavidSouther - 我是一名长期的 Java 开发人员,最近一直在使用 groovy。而且我继续告诉人们:朋友不要让朋友写 bash 脚本。然而,我发现自己在看这个帖子/解决方案,因为(悲伤的脸:()我在 bash 中从事并行处理。我可以很容易地在 groovy/java 中做到这一点。糟糕!

标签: bash sh xargs


【解决方案1】:

似乎我不能制作 cmets :-(

我想知道重点是

bash -c 'echo_var "$@"' _ {}
vs
bash -c 'echo_var "{}"'

第一个将 {} 替换为 bash 的 arg,而第二个替换为函数的 arg。示例 1 没有扩展 $(date) 的事实只是一个副作用。

如果您不希望函数 args 扩展,请使用单引号而不是双引号。为避免嵌套混乱,请使用双引号(在另一个上扩展 args)

$ echo '$(date)' | xargs -0 -L1 -I {} bash -c 'printit "{}"'
Fri 11 Sep 17:02:24 BST 2020

$ echo '$(date)' | xargs -0 -L1 -I {} bash -c "printit '{}'"
$(date)

【讨论】:

    【解决方案2】:

    导出函数应该可以做到(未经测试):

    export -f echo_var
    seq -f "n%04g" 1 100 | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}
    

    您可以使用内置的printf 而不是外部的seq

    printf "n%04g\n" {1..100} | xargs -n 1 -P 10 -I {} bash -c 'echo_var "$@"' _ {}
    

    另外,使用return 0exit 0 可以屏蔽前面的命令可能产生的任何错误值。此外,如果没有错误,它是默认设置,因此有些多余。

    @phobic 提到 Bash 命令可以简化为

    bash -c 'echo_var "{}"'
    

    {} 直接移入其中。 但是正如@Sasha 所指出的,它容易被命令注入

    以下是您不应使用嵌入格式的示例:

    $ echo '$(date)' | xargs -I {} bash -c 'echo_var "{}"'
    Sun Aug 18 11:56:45 CDT 2019
    

    为什么不的另一个例子:

    echo '\"; date\"' | xargs -I {} bash -c 'echo_var "{}"'
    

    这是使用安全格式输出的内容:

    $ echo '$(date)' | xargs -I {} bash -c 'echo_var "$@"' _ {}
    $(date)
    

    这相当于使用parameterized SQL queries 来避免injection

    我在此处的命令替换或转义引号中使用 date,而不是 Sasha 评论中使用的 rm 命令,因为它是非破坏性的。

    【讨论】:

    • 再讨论一下:xargs 执行一个名为进程的全新实例。在这种情况下,您提供名称 echo_var,它是此脚本中的一个函数,而不是您的 PATH 中的一个进程(程序)。 Dennis 的解决方案是导出函数供 bash 子进程使用,然后分叉到子进程并在那里执行。
    • _\ 的意义是什么,没有它们对我不起作用
    • @Hashbrown:下划线 (_) 为 argv[0] ($0) 提供了一个占位符,几乎可以在其中使用任何东西。我想我添加了反斜杠分号(\;),因为它用于终止find 中的-exec 子句,但在这里没有它对我有用。事实上,如果函数使用$@ 而不是$1,那么它会将分号视为参数,所以应该省略它。
    • -i 参数 xargs 已被弃用。请改用 -I(大写 i)。
    • 您可以通过在带有bash -c 'echo_var "{}"' 的bash 的命令字符串中包含来自xargs 的参数来简化此操作。所以最后不需要 _ {}。
    【解决方案3】:

    使用 GNU Parallel 看起来像这样:

    #!/bin/bash
    echo_var(){
        echo $1
        return 0
    }
    export -f echo_var
    seq -f "n%04g" 1 100 | parallel -P 10 echo_var {} 
    exit 0
    

    如果您使用版本 20170822,您甚至不必export -f,只要您运行了这个:

    . `which env_parallel.bash`
    seq -f "n%04g" 1 100 | env_parallel -P 10 echo_var {} 
    

    【讨论】:

    • 在下面得到这个错误 Ole sh: parallel_bash_environment: line 67: unexpected EOF while looking for matching '' sh:parallel_bash_environment: line 79: 语法错误: 文件意外结束 sh: 为parallel_bash_environment' /usr/local/bin/bash: parallel_bash_environment: line 67: unexpected EOF while looking for matching ''/usr/local/bin 导入函数定义时出错/bash:parallel_bash_environment:第 79 行:语法错误:文件意外结束 /usr/local/bin/bash:为 ` ... 导入函数定义时出错
    • 你被shellaftershocked了:Shellshock并没有直接影响GNU Parallel。然而,shellshock 的解决方案确实做到了:它完全破坏了 --env 和 env_parallel 技巧。相信在git版本中已修复:git.savannah.gnu.org/cgit/parallel.git/snapshot/…
    • 我喜欢这个答案,因为它让我发现了并行工具
    【解决方案4】:

    也许这是不好的做法,但如果您在 .bashrc 或其他脚本中定义函数,您可以使用 allexport 的设置包装文件或至少函数定义:

    set -o allexport
    
    function funcy_town {
      echo 'this is a function'
    }
    function func_rock {
      echo 'this is a function, but different'
    }
    function cyber_func {
      echo 'this function does important things'
    }
    function the_man_from_funcle {
      echo 'not gonna lie'
    }
    function funcle_wiggly {
      echo 'at this point I\'m doing it for the funny names'
    }
    function extreme_function {
      echo 'goodbye'
    }
    
    set +o allexport
    

    【讨论】:

      【解决方案5】:

      这样的事情也应该起作用:

      function testing() { sleep $1 ; }
      echo {1..10} | xargs -n 1 | xargs -I@ -P4 bash -c "$(declare -f testing) ; testing @ ; echo @ "
      

      【讨论】:

      • 一般来说,这会破坏 shell 特殊字符(例如|#)并忽略输入中的空格。我建议不要让 bash 将输入视为代码,而是让 xargs 按原样传递它们。 echo {1..10} | xargs -n 1 -P4 bash -c "$(declare -f testing);"' testing "$@"; echo "$@";' argv0
      猜你喜欢
      • 2011-03-20
      • 2015-06-19
      • 2018-11-21
      • 1970-01-01
      • 1970-01-01
      • 2010-12-08
      相关资源
      最近更新 更多