/ bin / sh:variables和直接命令的区别

想象一下,我有以下bash脚本:

#/bin/sh #next line will work correctly, outputting user name and current directory start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls' MYVAR="start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls'" #next line will fail with following error: #ls': -c: line 0: unexpected EOF while looking for matching `'' $MYVAR 

问题 – 为什么第二种方法通过variables不起作用? 如何使其工作?

正如@lled所解释的那样,问题在于,在扩展variables之前,shell会处理引号,所以将引号放在variables中并没有什么用处。 但是有几个select,取决于你为什么要存储命令(而不是直接执行它)。

如果你只是想定义一次命令,那么重复使用它,使用一个函数:

 myfunc() { start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls' } # ... myfunc 

如果你需要在一个地方build立/select命令,然后在另一个地方使用它,你可以使用一个bash数组来存储它。 将命令的每个“单词”存储为一个数组元素,然后如果您正确引用它们,将保留这些分词符:

 myarray=(start-stop-daemon --start --exec /bin/su -- root -c 'whoami; ls') # ... "${myarray[@]}" 

这里发生的事情是数组被定义为具有“start-stop-daemon”,“ – start”,“–exec”,“/ bin / su”,“ – ”,“root”,“ -c“和”whoami; ls“。 单引号不作为数组的一部分存储,但是它们具有使“whoami; ls”成为单个数组元素的效果。 然后,在数组的扩展中, [@]指示shell将每个数组元素展开成一个单独的单词,并且围绕它的双引号可防止对结果值进行任何额外的单词分割。

有关更多信息(以及其他选项),请参阅BashFAQ#50:我试图将一个命令放在一个variables中,但复杂的情况总是失败!

Bourne(和Bourne再次)shell从行parsing到命令执行都有复杂的规则。

为了简化起见,让我们减less你的直接命令行

 su -c 'whoami; ls' 

和你的“embedded”的

 MYVAR="su -c 'whoami; ls'" $MYVAR 

在第一种情况下,命令行被分成3个标记(单词),因为空格字符是一个元字符分隔的单词,也是因为引号转义元字符(因此将空格转义为引号)。 在执行命令之前,应用报价移除阶段,留下3个字并且没有引号。 第一个字是命令名su ,另外两个是-cwhoami; ls whoami; ls ,命令参数。 对。

在第二种情况下,该调用由单个参数扩展组成。 对于一些扩展(包括参数扩展),适用分词。 为了这个目的,一个名为IFS的特殊variables被用来从扩展的参数值中推导出单词。 默认情况下,IFS由空格和制表符以及换行符组成。 这意味着每个这些字符都被用来分割一堆字符来构成单词。 这里的扩展值是:

 su -c 'whoami; ls' 

我们最终得到4个词,即: su-c'whoami;ls' 。 最重要的是,报价清除阶段不会发生参数展开值。 命令被发出(命令名为su和它的3个参数),并且你得到一个奇怪的错误信息。

这种情况的复杂性在于,我们必须保留一个含有空格的词作为su的参数,空格本身暴露在分词阶段,引用在参数扩展中是无用的。

你能做些什么来处理这个问题就是玩IFSvariables。 一个解决scheme是:

 IFS=: MYVAR="su:-c:whoami; ls" $MYVAR 

IFS在这里被覆盖,以便从分词中排除空格字符,该任务由新引入的冒号字符来承担。