使用命令替换作为cp命令参数失效,求解决方案及替代方法
问题原因分析
没错,就是命令替换的参数传递逻辑导致的问题。bash执行命令替换$(...)时,会先把替换结果做分词处理——按空格、制表符、换行符拆分内容,但不会识别里面的转义符(比如\)或者引号。
你用printf '%q ' "${fs[@]}"输出的是bar\ foo foo\ bar,命令替换后,bash会把这串内容拆成bar\、foo、foo\、bar四个单独的参数传给cp,而不是你期望的bar foo和foo bar两个参数。这就导致cp找不到bar\和foo这些文件,自然报错。
而直接写bar\ foo时,bash会把它解析成单个带空格的文件名,因为转义符是在bash解析命令行时处理的,不是在命令替换之后。
解决方案
最安全、最简单的方案是直接传递数组元素,完全不需要转义或命令替换:
cp -r "${fs[@]}" bar
"${fs[@]}"是bash数组的标准展开方式,会把数组里的每个元素作为独立参数传递,不管元素里有没有空格、特殊字符,都能被cp正确识别为单个文件名,完美解决问题。
关于eval的使用
如果因为某些特殊场景(比如必须处理转义后的字符串)不得不使用转义结果,确实可以用eval,但要注意风险:
eval cp -r $(printf '%q ' "${fs[@]}") bar
eval会重新解析整个命令行,识别转义符和引号,把bar\ foo正确解析为单个文件名。但eval的风险在于,如果数组里包含恶意内容(比如; rm -rf /这类命令),会被直接执行,所以非必要情况下绝对不要用。
内容的提问来源于stack exchange,提问作者Signor Pizza
相关产品推荐
相关产品推荐

