我有一个命令输出一些我想传递给另一个命令的参数。但是,当我在子 shell 中运行命令时,输出会受到分词的影响,除非我引用整个内容,在这种情况下它是一个单词。
我希望 subshell 输出有单词拆分,但我希望单词拆分尊重引号,但事实并非如此。有没有办法(除了eval
)将子shell输出分成单词但尊重引号?
细节
给定args
定义为的命令
#!/bin/sh -
printf "%d args:" "$#"
printf " <%s>" "$@"
echo
我可以跑
$ args 'foo bar' 'one two'
2 args: <foo bar> <one two>
但我找不到办法让子shell 像这样传递 2 个参数。
$ echo "\"'foo bar'\" 'one two'"
"'foo bar'" 'one two'
$ args $(echo "\"'foo bar'\" 'one two'")
4 args: <"'foo> <bar'"> <'one> <two'>
$ args "$(echo "\"'foo bar'\" 'one two'")"
1 args: <"'foo bar'" 'one two'>
当然,我可以使用eval
$ eval args $(echo "'foo bar' 'one two'")
2 args: <foo bar> <one two>
但是eval
很危险,并且会引入各种其他可怕的可能性,导致事情出错。我不希望再次发生参数扩展或通配符等。我只想分词以尊重引号。真的没有办法做到这一点bash
吗?
Bash 在尊重引号的同时确实没有将字符串解析为子字符串的好方法。无论是来自命令扩展(即
$( )
——我认为您称之为子shell)还是普通变量扩展($varname
)。eval
在双引号扩展中使用,它会解析所有shell 语法,包括其他命令和变量扩展、重定向、使用;
or的多个命令&
等。这里有很多可能导致不良结果。eval
在未引用的扩展上使用,您会得到 split-on-whitespace-and-expand-wildcards 效果,然后是常规的完整解析。这里几乎所有事情都可能出错。read -a
是最好的。它在尊重引号方面完全失败,但至少它不会扩展文件名通配符。所以 bash 本身不能做到这一点。但是
xargs
可以 - 它的默认拆分成单词解析尊重引号和转义,因此根据情况您可以直接使用它:这有几个潜在的问题:一方面,取决于有多少输出,
xargs
可能决定在命令的多次运行之间拆分它们。-n
您可以使用它的、-s
和选项在一定程度上对其进行调整-x
,但这并不完全令人满意。另一个可能的问题是,如果该命令实际上是一个 shell 函数、复杂命令或您想在当前 shell 中执行的内置命令,那么这将不起作用。你可以调整它,但它很乱;您需要使用
xargs printf '%s\0'
转换为以 null 分隔的字符串序列,然后使用while IFS= read -r -d ''
循环将其转换为 bash 数组,最后您可以对数组执行某些操作:请注意,这使用
<( )
. 这是一个仅限 bash 的功能,甚至在 bash 处于 sh 仿真模式时也不起作用。因此,您必须使用显式的 bash shebang (#!/bin/bash
或#!/usr/bin/env bash
) 开始您的脚本(并且不要通过使用 运行脚本来覆盖 shebangsh scriptname
)。