因此,我正在编写一个BASH shell脚本,为我正在处理的Node项目执行一些CLI测试(我没有在这个问题中标记Node,因为实际上这只与BASH有关);我的CLI测试如下所示:
test_command=$'node source/main.js --input-regex-string \'pcre/(simple)? regex/replace/vim\' -o';
echo $test_command;
$test_command 1>temp_stdout.txt 2>temp_stderr.txt;
test_code=$?;
echo "test_code $test_code"
test_stdout=`cat temp_stdout.txt`;
test_stderr=`cat temp_stderr.txt`;
正如你所看到的,我使用了C风格的引号$'...'
,作为described here,这应该使$test_command
字面上扩展为node source/main.js --input-regex-string 'pcre/(simple)? regex/replace/vim' -o
,这 * 是 * 第2行的echo
所显示的,但是当我试图运行第3行的命令时,我会得到一个错误,说regex/replace/vim'
在我的脚本中不是一个可识别的命令行参数。显然,这里发生的事情是,尽管我似乎正确地引用和转义了所有内容,BASH仍然将regex/replace/vim'
部分拆分为自己的word。根据我所读到的关于BASH引用和单词拆分规则的所有内容,这本不应该发生,但它却发生了。我试着将第一行的引号改为使用strong/literal '
引号('node source/main.js --input-regex-string "pcre/(simple)? regex/replace/vim" -o'
,这只会导致第3行将整个内容视为一个单词,因此不起作用)和弱/动态"
引号("node source/main.js --input-regex-string 'pcre/(simple)? regex/replace/vim' -o"
与强引号示例完全相同,更不用说由于在这种情况下引用的字符串是正则表达式文字,它不适合"
的神奇扩展行为)来代替C风格的引号,更改命令字符串本身的转义以适应所使用的引号风格;我曾尝试在字符串中添加转义符,如test_command=$'node source/main.js --input-regex-string \\\'pcre/(simple)?\ regex/replace/vim\\\' -o
,但结果却完全相同;我试着改变调用第3行命令的方式:引用扩展,将其封装在{ ... }
或${ ... }
中,并结合前面提到的变体,所有这些仍然导致原始的单词拆分问题或我只是被给予一般的“坏替换”语法错误。
**所以,简而言之,**我的问题是调用/格式化命令的正确方法是什么,作为字符串存储在BASH变量中,包含一个引用的文字字符串,BASH不会莫名其妙地将所包含的引用字符串拆分并中断整个命令?
2条答案
按热度按时间xkrw2x1b1#
调用/格式化命令的正确方法是什么,该命令作为字符串存储在BASH变量中,包含一个带引号的文字字符串
你假设这两者之间没有区别
1.直接在终端/脚本中键入命令
1.将完全相同的命令字符串存储到变量中,然后执行
$variable
。但是有很多区别!直接输入bash的命令比其他任何命令都要经历更多的处理步骤。这些步骤在bash's manual中有记录:
1.标记化
引号被解释。运算符被标识。命令被拆分成单词,单词之间的空格被括起来。这里不使用
IFS
。1.以从左到右的方式进行了几次扩展。也就是说,在对令牌应用了这些转换之一之后,bash将继续使用3处理其结果。例如,您可以安全地使用路径名中带有文字
$
的主目录,因为扩展~
的结果不会进行变量扩展,因此$
保持未解释。{1..9}
~
$var
$((...))
$(...)
、...
<()
1.分词
使用
IFS
拆分未加引号的展开式的结果。1.文件名展开
也称为globbing:
*
、?
、[...]
以及更多的shopt -s extglob
。诚然,这让大多数bash初学者感到困惑。对我来说,Stackoverflow的大多数bash问题都是关于这些处理步骤的。一些经典的例子是[
for i in {1..$n}
does not work][2]和[echo $var
does not print what I assigned tovar
][3]。来自未加引号的变量的字符串只经历上面列出的一些处理步骤。如上所述,这些步骤是 “3. word splitting” 和 “4. filename expansion”。
如果你想对一个字符串应用所有的处理步骤,你可以使用
eval
命令。然而,这是非常令人不快的,因为要么有更好的替代方案(如果你自己定义命令),要么有巨大的安全隐患(如果一个局外人定义命令)。在你的例子中,我看不出有什么理由存储命令。但是如果你真的想在其他地方以字符串的形式访问它,那么就使用数组:
ovfsdjhp2#
什么是调用/格式化命令的正确方法,作为字符串存储在BASH变量中,包含一个引用的文字字符串,BASH不会莫名其妙地将所包含的引用字符串拆分并中断整个命令?
“正确”的方法(对我来说)是 * 不是 * 将命令作为字符串存储在变量中。正确的方法是使用一个函数,该函数还允许在内部添加任何逻辑:
正确的方法是将其存储为数组:
一个 * 现有 * 的方法是使用
eval
which is evil将存储的命令作为变量中的字符串运行。您可以 * 正确地 * 转义参数并将它们连接到字符串,然后使用eval
执行它:这本不该发生的事却发生了
对以下对象执行字拆分:
shell扫描参数展开、命令替换和算术展开的结果,如果这些结果没有出现在双引号中,则进行单词拆分。
由参数展开 * 产生 * 的双引号或单引号并不特殊,它们是按字面意思理解的。只有当参数展开本身在双引号内时才重要。因为在您的代码片段中
$test_command
不在双引号内,所以结果是单词拆分的,这是:Shell将$IFS的每个字符视为分隔符,并使用这些字符作为字段终止符将其他扩展的结果拆分为单词。
它不关心引号。它在确定哪个参数进行单词拆分时关心它们-那些不在双引号内的参数。如果参数进行单词拆分,结果只是粗略地在空格上拆分,引号在那里并不特殊。