“ echo $(stuff)”或“ echo`stuff`”有什么问题?


31

我使用以下之一

echo $(stuff)
echo `stuff`

stuff例如pwddate或更复杂的地方)。

然后我被告知,这种语法是错误的,是一种错误的做法,不雅致,过多,冗余,过于复杂,不道德的编程,无礼,天真等。

但是该命令确实有效,那么究竟出了什么问题呢?


11
这是多余的,因此不应使用。与猫的无用使用作比较:porkmail.org/era/unix/award.html
dr01

7
echo $(echo $(echo $(echo$(echo $(stuff)))))可以使用,但您仍然可能不会使用它,对吗?;-)
彼得-恢复莫妮卡

1
您到底打算做什么?
curiousguy

@curiousguy我正在尝试帮助其他用户,因此我在下面给出了答案。最近,我已经看到至少三个使用此语法的问题。他们的作者正在尝试……各种各样stuff。:D
卡米尔Maciorowski

2
另外,我们所有人都不同意将自己局限于回声室是不明智的吗?;-)
彼得-恢复莫妮卡

Answers:


63

tl; dr

鞋底stuff最有可能为你工作。



完整答案

怎么了

运行时foo $(stuff),会发生以下情况:

  1. stuff 奔跑
  2. 它的输出(stdout)而不是被打印,而是$(stuff)foo; 的调用中被替换。
  3. 然后foo运行,其命令行参数显然取决于stuff返回的内容。

这种$(…)机制称为“命令替换”。在您的情况下,main命令echo基本上将其命令行参数输出到stdout。因此,任何stuff尝试打印到stdout的东西都会被捕获,传递echo给stdout并打印到stdout echo

如果要将输出输出stuff到stdout,只需运行sole即可stuff

`…`语法有异曲同工之妙的$(…)(在同一个名字:“命令替换”),也有虽然很少差异,所以不能盲目地交换他们。请参阅此常见问题解答此问题


echo $(stuff)无论如何我都应该避免吗?

echo $(stuff)如果您知道自己在做什么,则可能要使用一个原因。出于同样的原因,echo $(stuff)如果您真的不知道自己在做什么,应该避免使用。

重点是stuffecho $(stuff)而且并不完全相同。后者意味着stuff在默认值为的输出上调用split + glob运算符$IFS。用双引号代替命令可以防止这种情况。单引号命令替换使它不再是命令替换。

要在拆分时观察到这一点,请运行以下命令:

echo "a       b"
echo $(echo "a       b")
echo "$(echo "a       b")"  # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "a       b")'

并为:

echo "/*"
echo $(echo "/*")
echo "$(echo "/*")"  # the shell is smart enough to identify the inner and outer quotes
echo '$(echo "/*")'

如您所见echo "$(stuff)",等效于(-ish *)stuff。您可以使用它,但是以这种方式使事物复杂化的目的是什么?

在另一方面,如果你想要的输出stuff经过拆分+通配符,那么你可能会发现echo $(stuff)有用的。但是,这必须是您的自觉决定。

有一些命令生成应评估的输出(包括拆分,globbing等)并由外壳程序运行,因此eval "$(stuff)"有可能(请参见此答案)。我从来没有见过一个需要它的输出被前进行额外的拆分+通配符命令打印。故意使用echo $(stuff)似乎很少见。


var=$(stuff); echo "$var"

好点子。此代码段:

var=$(stuff)
echo "$var"

应该等价于echo "$(stuff)"(-ish *)stuff。如果是完整的代码,请stuff改为运行。

但是,如果您需要stuff多次使用输出,则此方法

var=$(stuff)
foo "$var"
bar "$var"

通常比

foo "$(stuff)"
bar "$(stuff)"

即使fooecho并且您echo "$var"输入了代码,最好还是这样保存。注意事项:

  1. 随着var=$(stuff) stuff运行一次; 即使命令很快,避免两次计算相同的输出也是正确的选择。也许stuff除了写入stdout以外还具有其他效果(例如,创建临时文件,启动服务,启动虚拟机,通知远程服务器),因此您不想多次运行它。
  2. 如果stuff生成依赖时间或随机的输出,您可能会从foo "$(stuff)"和获得不一致的结果bar "$(stuff)"。之后var=$(stuff)的价值$var是固定的,你可以肯定foo "$var",并bar "$var"得到相同的命令行参数。

在某些情况下,foo "$var"您可能不希望使用()foo $var,尤其是当stuff生成多个参数时foo(如果shell支持,数组变量可能会更好)。再次,知道你在做什么。当谈到echo的区别echo $varecho "$var"相同之间echo $(stuff)echo "$(stuff)"


*等价的(-ish)?

我说echo "$(stuff)"等同于(-ish)stuff。至少有两个问题使其不完全等效:

  1. $(stuff)stuff在subshel​​l中运行,因此最好说echo "$(stuff)"等于(-ish)(stuff)。影响它们在其中运行的shell的命令(如果在子shell中)不会影响主shell。

    在这个例子中stuffa=1; echo "$a"

    a=0
    echo "$(a=1; echo "$a")"      # echo "$(stuff)"
    echo "$a"
    

    与它比较

    a=0
    a=1; echo "$a"                # stuff
    echo "$a"
    

    a=0
    (a=1; echo "$a")              # (stuff)
    echo "$a"
    

    再举一个例子,首先stuffcd /; pwd

    cd /bin
    echo "$(cd /; pwd)"           # echo "$(stuff)"
    pwd
    

    以及测试stuff(stuff)版本。

  2. echo这不是显示不受控制的数据的好工具echo "$var"我们正在谈论的本该是printf '%s\n' "$var"。但是由于提到了问题,echo并且因为最可能的解决方案不是一开始就使用echo,所以我决定在此printf之前不作介绍。

  3. stuff(stuff)将交错stdout和stderr输出,而echo $(stuff)将打印来自的所有stderr输出stuff(首先运行),然后才打印摘要的stdout输出echo(最后运行)。

  4. $(…)删除所有结尾的换行符,然后echo将其重新添加。因此echo "$(printf %s 'a')" | xxd给出的输出与的输出不同printf %s 'a' | xxd

  5. ls根据标准输出是否是控制台,某些命令(例如)的工作方式有所不同。所以ls | cat不一样ls。同样的echo $(ls)工作方式与ls

    ls放在一边,在一般情况下,如果你非要逼那么这等行为stuff | cat是优于echo $(ls)echo "$(ls)"因为它不引发其它的所有问题,这里提到。

  6. 可能的退出状态(此Wiki答案的完整性所提及;有关详细信息,请参阅另一个值得赞赏的答案)。


5
另一个区别是:stuff方式将交错stdoutstderr输出,而echo `stuff` 将打印stderr来自的所有输出stuff,然后仅显示stdout输出。
罗斯兰

3
这是另一个示例:bash脚本中几乎不会有太多的引号。echo $(stuff)可能会给您带来更多麻烦,echo "$(stuff)"好像您不想显式地进行遍历和扩展一样。
rexkogitans

2
还有一点细微的区别:$(…)剥离所有尾随的换行符,然后echo将其重新添加。因此echo "$(printf %s 'a')" | xxd给出的输出与的输出不同printf %s 'a' | xxd
derobert

1
您不应忘记某些命令(ls例如)的工作方式有所不同,具体取决于标准输出是否为控制台。所以ls | cat不一样ls。当然,它的echo $(ls)工作原理将不同于ls
Martin Rosenau '18

@Ruslan答案现在是社区Wiki。我已将您的有用评论添加到Wiki。谢谢。
卡米尔Maciorowski

5

另一个区别:子外壳退出代码丢失了,因此echo取而代之的是退出代码。

> stuff() { return 1
}
> stuff; echo $?
1
> echo $(stuff); echo $?
0

3
欢迎来到超级用户!您能解释一下您所展示的区别吗?谢谢
bertieb '18

4
我相信这表明返回码$?将是echo(for echo $(stuff))的返回码,而不是returned 的返回码stuff。该stuff函数return 1(退出代码),但echo无论的返回代码如何,都将其设置为“ 0” stuff。仍应在答案中。
伊斯梅尔·米格尔

子外壳的返回值已丢失
phuclv
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.