在Bash中,是否可以编写一个'identity'函数(相对于传递/返回的参数)

Luc*_*mon 3 variables bash quotes arguments expansion

这种"身份"功能应满足以下2个属性:

identity $(identity a\ b c\ d)
# (Expected output:)
# a b
# c d
Run Code Online (Sandbox Code Playgroud)

并且,给定以下'argv_count'函数:

argv_count () { echo "argv_count('$@'):$#"; }
argv_count $(identity a\ b c\ d)
# (Expected output:)
# argv_count('a b c d'):2
Run Code Online (Sandbox Code Playgroud)

如果需要,可以在测试中引入额外的报价.

如下所示的简单候选者未能通过第二次测试:

identity () { for arg in "$@"; do echo "$arg"; done; }
Run Code Online (Sandbox Code Playgroud)

cat不是正确的解决方案,因为它是相对于stdin | stdout的标识函数.

Gor*_*son 6

不,这是不可能的.原因是当shell解析命令替换的输出(即$(somecommand))时,它执行分词和通配符扩展但没有引用或转义评估.这意味着如果identity输出包含空格,shell 将把它视为"单词"(即其他程序的参数)之间的分隔符,无论引用/转义/您添加的任何内容都试图避免这种情况.更糟糕的是,输出中包含任何带通配符的单词被扩展为匹配文件列表(如果有的话).这意味着$(identity 'foo * bar')双重注定要失败.

话虽如此,有一些方法可以通过更改shell设置来伪造它.例如,set -f将关闭通配符扩展,解决该问题 - 除了必须在运行之前在父shell中identity设置它,然后将其设置回正常或许多其他事情将中断.类似地,您可以更改IFS以防止将单词拆分视为分隔符 - 但是您必须在父shell中更改它并在之后将其设置回来,然后它会给您选择的替换分隔符造成麻烦.所以你可以假装它,但这是非常糟糕的伪造.

编辑:正如Michael Kropat所指出的那样,使用eval是另一种"伪造"它的方法,如果仔细地做,则更灵活.