如何使用xargs在每个匹配项的命令替换中运行函数?

时间:2019-02-19 14:14:09

标签: linux bash function sed xargs

在编写用于替换字符串的Bash函数时,我在使用xargs时遇到了奇怪的行为。目前,这实际上使我发疯,因为我无法正常工作。 幸运的是,我能够将其固定为以下简单示例:

定义一个简单函数,将给定参数的每个字符加倍:

function subs { echo $1 | sed -E "s/(.)/\1\1/g"; }

调用函数:

echo $(subs "ABC")

如预期的那样,输出为:

AABBCC

现在使用xargs调用函数:

echo "ABC" | xargs -I % echo $(subs "%")

令人惊讶的是,现在的结果是:

ABCABC

函数内部的sed命令似乎现在将整个字符串视为单个字符。 为什么会发生这种情况,如何预防呢?

您可能会问,为什么我完全使用xargs。当然,这是一个简化的示例,实际用例要复杂得多。

在原始用例中,我有一个程序会产生大量输出。我通过几次抓取来输出输出,以获得感兴趣的行。之后,我通过管道将sed提取出来。由于我需要对数据进行的某些转换过于复杂,无法单独使用正则表达式,因此我想对这些使用函数。因此,我最初的想法是简单地将其插入函数中,但是我无法使其正常工作并最终得到xargs解决方案。我最初的想法是这样的:

command | grep ... | grep ... | grep ... | sed ... | subs

顺便说一句:我不是从命令行而是从脚本中执行此操作。该功能是在使用该脚本的相同脚本中定义的。

我使用的是Bash 3.2(默认为Mac OS X),所以花哨的Bash 4.x内容对我无济于事。

我会对所有可能使您对该主题有所了解的事情感到高兴。

最诚挚的问候

坦率

2 个答案:

答案 0 :(得分:2)

如果您真的需要这样做(您可能不需要,但是没有一个更具代表性的样本我们就无济于事),一种更好的做法可能是:

subs() { sed -E "s/(.)/\1\1/g" <<<"$1"; }
export -f subs

echo "ABC" | xargs bash -c 'for arg; do subs "$arg"; done' _
  • 使用echo "$(subs "$arg")"而不是subs "$arg"只会增加bug(请考虑一下,如果您的参数之一为-n会发生什么,并且假设相对温和的{{1} };即使没有echo参数,也可以使用反斜杠,并且可以进行其他各种令人惊讶的事情)。您可以 在上面完成此操作,但是它会使程序运行变慢,并且更容易出现令人惊讶的行为。没有意义。
  • 运行-e会将您的函数导出到环境中,因此它可以由作为子进程调用的bash的其他实例运行(export -f subs调用的所有程序都在您的外壳程序之外,因此它们不能参见shell局部变量或函数)。
  • 没有xargs -也就是说,在其默认操作模式下– -I将参数附加到给出的命令的末尾。这样可以提供一种更高效的使用模式,该模式无需将输入的尽可能多的参数传递给尽可能短的子进程,而不必每行输入调用一个命令。

    这还避免了将xargsxargs -Ibash -c '...'结合使用时可能发生的主要安全错误。 (如果您曾经使用sh -c '...',则文件名将成为代码的一部分,并且可以在系统的注入攻击中使用。)

答案 1 :(得分:1)

这是因为构造$(subs "%")在解析管道时会被外壳扩展,因此xargsecho %%一起运行。