【问题标题】:Variable assignment in command substitution命令替换中的变量赋值
【发布时间】:2018-03-23 15:28:19
【问题描述】:

假设我们有一个命令输出一个变量赋值字符串,比如'var=foo',如果我把这个命令放在命令替换中,看起来像 $(echo var=foo),会导致“command not found”错误。

[223212 dot@anne ~]$ var=foo
[223226 dot@anne ~]$
[223230 dot@anne ~]$ $(var=foo)
[223235 dot@anne ~]$
[223236 dot@anne ~]$ $(echo var=foo)
bash: var=foo: command not found
[223240 dot@anne ~]$
[224909 dot@anne ~]$ $(echo ls)
a    b    c   d    
[225036 dot@anne ~]$
[225110 dot@anne ~]$ $(echo $(var=foo))
[225116 dot@anne ~]$

既然我们可以像这样$(var=foo)(虽然我认为这毫无意义),我们可以直接在命令替换中进行变量赋值,并且$(echo ls)也可以正常工作, 为什么在命令替换中输出赋值导致错误?

这是man bash关于命令替换:

命令替换允许命令的输出替换 命令名称。

Bash 通过在 a 中执行命令来执行扩展 subshel​​l 环境并将命令替换替换为 命令的标准输出,所有尾随换行符都被删除。

据我了解,$(echo var=foo) 应该替换为 var=foo,就像 $(var=foo) 一样。

我理解错了吗?

【问题讨论】:

  • 当您使用命令替换时,您应该将输出分配给某些东西,即foo=$(echo buzz)。当然“echo”在这里没有意义,它通常不需要命令替换,你通常只需使用foo=buzz
  • 这很有趣,看起来 Bash 将子进程的输出视为命令名称,而不是其他指令。就像您输入了var\=foo,因此找不到命令错误。尝试评估结果以解决此问题:eval $(echo var=foo).
  • 我知道这没有意义,而且我知道我可以使用 eval 来解决这样的问题。但这不是重点。使用 echo 只是一个简单的例子,实际上我使用的是 $(gpg -qd file)。
  • 如果您使用的是$(pgp -qd file),那么您可能会遇到BashFAQ #50 中详细描述的问题(因为从不带引号的命令替换生成的命令具有完全相同的警告)通过不带引号的字符串扩展生成)。
  • @Pawamoy,这不足为奇——唯一没有引用的扩展结果经过的解析阶段是字符串拆分和通配;在该点之前识别分配。 eval 可能是 OP 想要的,但它是 dangerous,最好不要在没有明确警告的情况下推荐。

标签: bash


【解决方案1】:

这里是man bash

SIMPLE COMMAND EXPANSION
   When a simple command is executed, the shell performs the fol‐
   lowing expansions, assignments, and redirections, from left to
   right.

   1.     The  words  that  the  parser  has  marked  as variable
          assignments (those  preceding  the  command  name)  and
          redirections are saved for later processing.

   2.     The words that are not variable assignments or redirec‐
          tions are expanded.  If any words remain  after  expan‐
          sion,  the  first  word  is taken to be the name of the
          command and the remaining words are the arguments.
   [...]

在你的例子中,简单的命令只有一个单词$(echo var=foo)

由于没有标记为变量赋值的词(因为这个词是命令替换),所以第 1 步不适用。

然后我们进入第 2 步,将单词 $(echo var=foo) 扩展为 var=foo。我们不回到第一步,我们只是按照第 2 步所说的:“将第一个单词作为命令的名称”。

这就是为什么var=foo 被作为命令执行而不是被解释为赋值的原因。

【讨论】:

  • 如果是这样,$(var=foo) 怎么样? $(var=foo) 是一个单词,显然也不会被标记为变量赋值,然后到第 2 步...
  • @dotc, var=foo 运行时没有输出,所以$(var=foo) 等价于$(),它什么都不做。
  • @hacks echo var=foo 中有两个词,而$(echo var=foo) 中只有一个词(一个命令替换,不管它包含什么)。
  • @hacks $(echo var=foo) 将变为 var=foo。它不会变成$(var=foo)echo var=foo 输出 var=foo,但在任何意义上都不会被var=foo 取代。
  • @hacks $(echo var=foo) 是一个简单的命令,只有一个单词。在第 1 步中,我们检查分配和重定向。 $(echo var=foo) 不是赋值或重定向,它只是一个命令替换。然后我们继续第 2 步。作为扩展的一部分,我们运行echo var=foo,捕获其结果(字符串var=foo),并替换它。我们现在有了var=foo这个词。但是,我们不会从顶部开始,并将其解析为作业。我们仍然在第 2 步。第 2 步说“如果还有任何单词 [..],则第一个单词被视为命令的名称”,因此我们将其作为 cmd 运行
猜你喜欢
  • 2013-12-08
  • 1970-01-01
  • 1970-01-01
  • 2012-03-20
  • 1970-01-01
  • 2012-12-12
  • 1970-01-01
  • 2016-03-19
  • 1970-01-01
相关资源
最近更新 更多