【问题标题】:xargs not working with built in shell functionsxargs 不适用于内置的 shell 函数
【发布时间】:2015-06-19 03:07:37
【问题描述】:

我正在尝试加快数据库的处理速度。我迁移到 xargs。但我被严重卡住了。如果 xargs 调用的命令不是内置的,则将参数列表传递给 xargs 不起作用。我不知道为什么。这是我的代码:

#!/bin/bash

list='foo
bar'

test(){
    echo "$1" 
}

echo "$list" | tr '\012' '\000' | xargs -0 -n1 -I '{}' 'test' {}

所以根本没有输出。并且测试功能永远不会被执行。但是,如果我将“xargs”命令中的“test”替换为“echo”或“printf”,它就可以正常工作。

【问题讨论】:

  • xargs 将可执行文件作为参数(包括自定义脚本)而不是环境中定义的函数。这可能有助于解释更大的问题。
  • 谢谢。将我的命令放入脚本中解决了这个问题。
  • 酷。我会正确回答,所以我们可以关闭这个问题:)
  • 顺便说一句,不要误称自己的东西test;它是一个内置的 shell(也称为[)。
  • 顺便说一句,您的问题描述完全错误。 xargs 不能使用内置命令,但它可以使用任何外部命令,例如 lsecho(这也是许多现代 shell 的内置命令,但仍可作为 /bin/echo 使用,以便,嗯, 这个工作)或printf.

标签: bash parameter-passing xargs


【解决方案1】:

您不能将 shell 函数直接传递给 xargs,但可以调用 shell。

printf 'foo\0bar\0' |
xargs -r -0 sh -c 'for f; do echo "$f"; done' _

sh -c '...'里面的东西可以任意复杂;如果你真的想要,你可以声明然后使用你的函数。但由于它简单且非递归,我只是内联了功能。

虚拟下划线参数是因为sh -c 'script'之后的第一个参数用于填充$0

因为您的问题似乎是关于优化的,我想您不想为传递给xargs 的每个项目生成一个单独的外壳——如果您这样做了,那么没有什么会变得更快。所以我放入了for 循环并将-I 等参数取出到xargs

【讨论】:

  • 还要注意-r 以防止xargs 在收到空输入时运行任何东西。
【解决方案2】:

xargs 将可执行文件作为参数(包括自定义脚本)而不是环境中定义的函数。

将您的代码移至脚本或使用xargs 将参数传递给外部命令。

【讨论】:

    【解决方案3】:

    更改自:

    echo "$list" | tr '\012' '\000' | xargs -0 -n1 -I '{}' 'test' {}
    

    收件人:

    export -f test
    echo "$list" | tr '\012' '\000' | xargs -0 -n1 -I '{}' sh -c 'test {}'
    

    【讨论】:

      【解决方案4】:

      我在 bbs.archlinux.org 网站上看到了来自 'jac' 的解决方案,该解决方案使用非常有效的主要和次要(从)脚本对。与通常接受单个 $1 参数的内部“函数”不同,主节点将参数列表发送到其辅助节点,其中 while 循环将列表的每个成员作为连续的 $1 值处理。这是我用来将“文件”命令应用于一堆可执行文件的示例对,在我的情况下,这些文件都以文件名中的“em”开头。根据需要进行更改:

      #!/bin/bash
      # primary:  showfil
      ls -l em* | grep  '^-rwx' | awk '{$1=$2=$3=$4=$5=$6=$7=$8=""; print $0}' | xargs -I% ~/showfilf "%"
      ~/showfilf fixmstr spisort trc
      exit 0
      
      #!/bin/bash
      # secondary:  showfilf
      myarch=$(uname -s | grep 'arwin')
      while [[ -n "$1" ]]; do
        if [ -x "$1" ]; then
           if [ -n "$myarch" ]; then
              file "./$1"
           else
              myfile=$(file "./$1" | awk '{print $1" "$3" "$10" "$11" "$12}')
              myfile=${myfile%(uses}
              myfile=${myfile%for}
              echo "$myfile"
           fi
        fi
        shift
      done
      exit 0
      

      此代码适用于 Darwin (Mac) 和 Linux,可能还有其他系统。主目录中的“grep”只保留可执行文件,不保留目录或符号链接。 'awk' 消除了 'ls' 的前 8 个字段,只保留了传递给 'xargs' 的文件名,它构建了一个引用文件名列表以发送到 'showfilf'。列表中有一个单独的“showfilf”调用以及其他三个文件名。 'showfilf' 有一个处理列表的 while 循环。请注意,这里有依赖于系统的代码,由“uname -s”和“grep”确定。最后,使这些脚本可执行,并将它们放在您的 $PATH 中,例如 $HOME。如果您的 $PATH 不包含您的 $HOME,我建议您在 .bashrc 或 .bash_login 中修改它,如下所示:export PATH=$PATH:$HOME

      【讨论】:

        最近更新 更多