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

How can I use xargs to run a function in a command substitution for each match?

在为字符串替换编写 Bash 函数时,我在使用 xargs 时遇到了一个奇怪的行为。这实际上让我发疯,因为我无法让它工作。 幸运的是,我已经能够将其归结为以下简单示例:

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

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

调用函数:

echo $(subs "ABC")

正如预期的那样,输出是:

AABBCC

现在使用 xargs 调用函数:

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

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

ABCABC

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

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

在最初的用例中,我有一个产生大量输出的程序。我通过几个 greps 管道输出以获得感兴趣的行。之后,我将这些行通过管道传输到 sed 以从这些行中提取我需要的数据。因为我需要对数据进行的一些转换太复杂而无法单独使用正则表达式,所以我想为这些使用一个函数。所以,我最初的想法是简单地通过管道输入函数,但我无法让它工作并最终得到 xargs 解决方案。我最初的想法是这样的:

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

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

我正在使用 Bash 3.2(Mac OS X 默认值),所以花哨的 Bash 4.x 东西对我没有帮助,抱歉.

我会很高兴看到任何可能对这个主题有所启发的事情。

此致

弗兰克

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

如果您真的需要这样做(您可能不需要,但如果没有更具代表性的样本我们无能为力),更好的实践方法可能看起来喜欢:

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

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

    这也避免了将 xargs -Ibash -c '...'sh -c '...' 结合使用时可能发生的重大安全漏洞。 (如果你曾经使用过 -I% sh -c '...%...',那么你的文件名就会成为你代码的一部分,并且能够用于对你的系统进行注入攻击)。