在编写用于替换字符串的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内容对我无济于事。
我会对所有可能使您对该主题有所了解的事情感到高兴。
最诚挚的问候
坦率
答案 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
将参数附加到给出的命令的末尾。这样可以提供一种更高效的使用模式,该模式无需将输入的尽可能多的参数传递给尽可能短的子进程,而不必每行输入调用一个命令。
这还避免了将xargs
与xargs -I
或bash -c '...'
结合使用时可能发生的主要安全错误。 (如果您曾经使用sh -c '...'
,则文件名将成为代码的一部分,并且可以在系统的注入攻击中使用。)
答案 1 :(得分:1)
这是因为构造$(subs "%")
在解析管道时会被外壳扩展,因此xargs
与echo %%
一起运行。