shell BASH:子命令扩展中不可避免的分词?

sbdsn5lh  于 2023-03-24  发布在  Shell
关注(0)|答案(2)|浏览(105)

因此,我正在编写一个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不会莫名其妙地将所包含的引用字符串拆分并中断整个命令?

xkrw2x1b

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 to var][3]。
来自未加引号的变量的字符串只经历上面列出的一些处理步骤。如上所述,这些步骤是 “3. word splitting”“4. filename expansion”
如果你想对一个字符串应用所有的处理步骤,你可以使用eval命令。然而,这是非常令人不快的,因为要么有更好的替代方案(如果你自己定义命令),要么有巨大的安全隐患(如果一个局外人定义命令)。
在你的例子中,我看不出有什么理由存储命令。但是如果你真的想在其他地方以字符串的形式访问它,那么就使用数组:

command=(node source/main.js --input-regex-string 'pcre/(simple)? regex/replace/vim' -o)
echo "${command[*]}" # print
"${command[@]}"      # execute
ovfsdjhp

ovfsdjhp2#

什么是调用/格式化命令的正确方法,作为字符串存储在BASH变量中,包含一个引用的文字字符串,BASH不会莫名其妙地将所包含的引用字符串拆分并中断整个命令?
“正确”的方法(对我来说)是 * 不是 * 将命令作为字符串存储在变量中。正确的方法是使用一个函数,该函数还允许在内部添加任何逻辑:

test_command() {
    node source/main.js --input-regex-string 'pcre/(simple)? regex/replace/vim' -o "$@"
}
test_command

正确的方法是将其存储为数组:

test_command=(node source/main.js --input-regex-string 'pcre/(simple)? regex/replace/vim' -o)
"${test_command[@]}"

一个 * 现有 * 的方法是使用eval which is evil将存储的命令作为变量中的字符串运行。您可以 * 正确地 * 转义参数并将它们连接到字符串,然后使用eval执行它:

test_command=$(printf "%q " node source/main.js --input-regex-string 'pcre/(simple)? regex/replace/vim' -o)
eval "$test_command"

这本不该发生的事却发生了
对以下对象执行字拆分:
shell扫描参数展开、命令替换和算术展开的结果,如果这些结果没有出现在双引号中,则进行单词拆分。
由参数展开 * 产生 * 的双引号或单引号并不特殊,它们是按字面意思理解的。只有当参数展开本身在双引号内时才重要。因为在您的代码片段中$test_command不在双引号内,所以结果是单词拆分的,这是:
Shell将$IFS的每个字符视为分隔符,并使用这些字符作为字段终止符将其他扩展的结果拆分为单词。
它不关心引号。它在确定哪个参数进行单词拆分时关心它们-那些不在双引号内的参数。如果参数进行单词拆分,结果只是粗略地在空格上拆分,引号在那里并不特殊。

相关问题