当扩大到命令变量的bash引号处理不同 [英] bash quotes in variable treated different when expanded to command

查看:222
本文介绍了当扩大到命令变量的bash引号处理不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过例子来解释这个问题...

Explaining the question through examples...

表明当变量被扩展--chapters后单引号是被转义(我没想到这一点):

Demonstrates that the single-quotes after --chapters is gets escaped when the variable is expanded (I didn't expect this):

prompt@ubuntu:/my/scripts$ cat test1.sh
#!/bin/bash
actions="--tags all:"
actions+=" --chapters ''"
mkvpropedit "$1" $actions

prompt@ubuntu:/my/scripts$ ./test1.sh some.mkv
Error: Could not open '''' for reading.

而现在由于某种原因mkvpropedit接收双引号作为文件名的一部分(我没想到这两种):

And now for some reason mkvpropedit receives the double quotes as part of the filename (I didn't expect this either):

prompt@ubuntu:/my/scripts$ cat test1x.sh
#!/bin/bash
command="mkvpropedit \"$1\""
command+=" --tags all:"
command+=" --chapters ''"
echo "$command"
$command

prompt@ubuntu:/my/scripts$ ./test1x.sh some.mkv
mkvpropedit "some.mkv" --tags all: --chapters ''
Error: Could not open '''' for reading.

上面的回显的命令似乎是正确的。把相同的文本在另一个脚本中给出了预期结果:

The above echo'd command seems to be correct. Putting the same text in another script gives the expected result:

prompt@ubuntu:/my/scripts$ cat test2.sh
#!/bin/bash
mkvpropedit "$1" --tags all: --chapters ''

prompt@ubuntu:/my/scripts$ ./test2.sh some.mkv
The file is being analyzed.
The changes are written to the file.
Done.

任何人都可以请解释为什么像预期的那样引号不行为。我搜索发现在这个问题上很难有在网络上许多其他报价的讨论。我甚至不知道该如何解释没有例子的问题。

Could anyone please explain why the quotes are not behaving as expected. I found searching on this issue difficult as there are so many other quoting discussions on the web. I wouldn't even know how to explain the question without examples.

恐怕有一天在参数文件名包含某些字符,打破了一切,因此可能过度引用。我不明白为什么当在脚本中直接输入或通过一个变量时提供相同的命令执行不同。请赐教。

I am afraid that some day the file name in the argument contains some character that breaks everything, hence the maybe excessive quoting. I do not understand why the same command executes differently when typed directly in the script or when provided via a variable. Please enlighten me.

感谢您的阅读。

推荐答案

要记住的重要一点是,报价只取出一次的,当命令行最初解析。其中插入命令行参数替代的结果的引号( $ foo的)或命令替换( $(CMD参数))不被视为一个特殊字符。 [注1]

The important thing to keep in mind is that quotes are only removed once, when the command line is originally parsed. A quote which is inserted into the command line as a result of parameter substitution ($foo) or command substitution ($(cmd args)) is not treated as a special character. [Note 1]

这似乎是从空白和水珠元字符不同。参数/命令替换(除非发生替换引号内)后,分词和路径扩展发生。 [注2]

That seems different from whitespace and glob metacharacters. Word splitting and pathname expansion happen after parameter/command substitution (unless the substitution occurs inside quotes). [Note 2]

其结果是,它几乎是不可能创造出一个bash变量的$ args 这样

The consequence is that it is almost impossible to create a bash variable $args such that

cmd $args

如果的$ args 包含报价,他们不会被删除。在的$ args 字由空格序列分隔的,不是单一的空格字符。

If $args contains quotes, they are not removed. Words inside $args are delimited by sequences of whitespace, not single whitespace characters.

做到这一点的唯一方法是设置 $ IFS 包括一些非空白字符;该字符然后可以在的$ args 用作单字符分隔符。然而,的没有办法引用值内的字符的,所以一旦你做到这一点,您所选择的字符不能用于除作为分隔符。这通常不是很理想。

The only way to do it is to set $IFS to include some non-whitespace character; that character can then be used inside $args as a single-character delimiter. However, there is no way to quote a character inside a value, so once you do that, the character you chose cannot be used other than as a delimiter. This is not usually very satisfactory.

有一个解决方案,但:庆典阵列

There is a solution, though: bash arrays.

如果您的$ args 到一个数组变量,那么你就可以展开反复引号语法:

If you make $args into an array variable, then you can expand it with the repeated-quote syntax:

cmd "${args[@]}"

产生的那些话每的$ args 元素只有一个字和副presses字的拆分和路径扩展,所以他们最终的文字。

which produces exactly one word per element of $args, and suppresses word-splitting and pathname expansion on those words, so they end up as literals.

所以,例如:

actions=(--tags all:)
actions+=(--chapters '')
mkvpropedit "$1" "${actions[@]}"

可能会做你想要什么。所以将:

will probably do what you want. So would:

args=("$1")
args+=(--tags)
args+=(all:)
args+=(--chapters)
args+=('')
mkvpropedit "${args[@]}"

等会

command=(mkvpropedit "$1" --tags all: --chapters '')
"${command[@]}"

我希望这是半透明的。

I hope that's semi-clear.

男人庆典(或网络版)包含一个窜吹账户的bash如何组装的命令,开始在部分扩容。这是值得一读一个完整的解释。

man bash (or the online version) contains a blow-by-blow account of how bash assembles commands, starting at the section "EXPANSION". It's worth reading for a full explanation.

注:


  1. 这不适用于评估或命令如的bash -c 这再次评估他们的论据后命令行处理。但是,这是因为命令行处理发生两次。

  1. This doesn't apply to eval or commands like bash -c which evaluate their argument again after command line processing. But that's because command-line processing happens twice.

词的拆分是不一样的分割命令成字,当命令被解析恰好。一方面,字拆分为使用分隔符 $ IFS 的值,而命令行解析使用空格。但这些都不是引号内完成的,所以他们在这方面相似。在任何情况下,单词被分割在这样或那样的前后参数替换

Word splitting is not the same as "dividing the command into words", which happens when the command is parsed. For one thing, word-splitting uses as separator characters the value of $IFS, whereas command-line parsing uses whitespace. But neither of these are done inside quotes, so they are similar in that respect. In any case, words are split in one way or another both before and after parameter substitution.

这篇关于当扩大到命令变量的bash引号处理不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆