为什么Shell Command Substitution吞噬了尾随的换行符?


25

按照下面的示例,以及我最近 在bash中的问题,结尾的换行符char哪里去了?,我想知道它为什么发生

  x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p 
# Output is: 610a62 
# The trailing newline from the 'echo' command
#   has been "deleted" by Command Substitution

我认为必须有一个非常重要的理由来执行shell动作,即Command Substitution,实际上从它正在替换的命令输出中删除一些数据...
但是我无法理解这一点,因为它似乎是它该做什么的对立面..即 将命令的输出传递回脚本过程...对我来说,隐瞒一个字符似乎很奇怪,但是我想这是有道理的...我很想找出那个原因。 。


Answers:


22

因为外壳最初并不是要成为一种完整的编程语言。

\n从某些命令输出中删除结尾非常困难。但是,出于显示目的,几乎所有命令的输出都以结束\n,因此……当您要在其他命令中使用它时,必须有一种简单的方法将其删除。$()选择的解决方案是通过结构自动移除。

因此,也许您会接受以下问题作为答案:

\n如果以下命令没有自动完成,您是否能找到一种简单的方法来删除结尾?

> echo The current date is "$(date)", have a good day!

请注意,需要使用引号来防止损坏在格式化日期中出现的双精度空格。


1
如果你说的是真的,那我一定会被惊呆。如果shopt可以更改您描述的默认行为,那么我会很高兴...我发现很难想到Bash / Linux / Unix会因为诸如“捕获标准输出”之类的关键功能而污染date它一个带有/不带有“ \ n”选项的选项...明显的解决方法是bash(或其他shell)shopt为此设置了一个...您知道吗?..您是否有任何参考链接说明此问题的原因和原因?...以及为什么没有date\ n选项。它应该!
Peter.O 2011年

2
在1979年的Unix“第七版”中,命令替换出现在Bourne shell第一个版本中。这已经是一次巨大的革命,我想(我不出生)没有人关心保持尾随“ \ n”。是的,您可以在不到10分钟的时间内阅读该联机帮助页,在那之前,似乎似乎没有任何关于命令替换的更改。除了首选的$()替代语法。
斯特凡希门尼斯

谢谢..我认为这可能是一个遗留问题,并且肯定正在形成这样一个事实,那就是我最好开始更仔细地观察我的Command Substitutions。直到今天,我必须一直在机翼上祈祷。.我遇到的一些“神秘事件”现在开始变得有意义了:) ...因此,在相反的情况下,您的“约会”假设是我想保持跟踪\ n,我必须编写类似这样的代码:( { echo -n "The current date is "; var="$(date; echo -e x)"; var="${var%x}"; echo -n "$var"; echo ", have a good day!"; }..就是这样:)
Peter.O 2011年

PS我再上面的评论:当然,刚才date的工作,但我只是用date作为一个例子任何命令..
Peter.O

您对日期的 “问题” 是我对“为什么”命令替代执行其工作的最强烈提示,因此,这是对我的问题的“最佳”回答……我当然也需要其他好的回答..
Peter.O 2011年

19

它是标准的一部分:

外壳程序应通过在子外壳程序环境中执行命令(请参见Shell Execution Environment)并用命令的标准输出替换命令替换项(命令文本加上“ $()”或反引号)来扩展命令替换,并删除替换结束时一个或多个<newline>的序列。

当然,标准可能是这样编写的,因为这是ksh它的工作方式或某种方式,但这是标准,并且它是记录的行为。如果您不喜欢它,请使用Perl或其他可以让您保持尾随换行符的方法。


一个不错的摘要..谢谢..并且链接肯定有帮助
Peter.O 2011年

4
标准之所以这样,是因为Bourne外壳就是这样做的,以后就是其他外壳。
吉尔(Gilles)“所以,别再邪恶了”,

6

好吧,这对我来说很有意义。在常规命令输出中,换行符仅位于第一位,因此在命令在新行中完成后,将出现提示。在大多数情况下,换行符都不是原始输出的一部分,它是用来整理屏幕的。当您解析命令的输出时,最后的换行通常很麻烦。为什么wc命令输出两行文本?哦,不是,它输出一个后跟换行符。当您解析时,wc您不必担心存在两行输出的事实-实际上并没有,只有一行。


@EightBitTony:我的问题与在终端中显示内容没有任何关系。如果要显示换行符,则它将显示换行符。此处的问题是,\n原始stdout流中的a 被Command Substitution删除。即。我的原始数据(非常重要的数据)已删除尾随的换行符,即使数据用“引号”括起来也是如此。我很合理地理解了单词拆分的工作原理和原因,但是我完全不知道为什么命令替换仅除去换行符 而仅除去尾随的行。
Peter.O 2011年

1
你没说什么改变了我的看法。通常,任何输出中的最后换行符纯粹是出于显示目的,而不是数据的一部分。
NovemberBitTony 2011年

我同意您的观点,并且一直以来...是的,在输出末尾的换行符为了方便...但是我的问题与之无关...我在问:为什么Command Substitution会强行删除它?...这是两个不同的问题。也许我的问题应该是:是否有内置的解决方法?。因为必须通过添加尾部的哑字符来编码此问题,所以太烂了!...如果需要的话,我会做的,但这是我第一次受到bash的困扰(而且我处于震惊状态:) ..效果如此之好……
Peter.O 2011年

如另一个答案中所述,它是标准的一部分。对于我来说,我认为这样做是因为在对数据进行后期处理时,您只想查看数据,而不是方便的换行符。这是因为shell 希望您在管道中处理数据,而换行符不是数据。再说了,我们需要对此进行讨论。
EightBitTony”,2011年

谢谢..我现在已经很了解这个主意了。这似乎是Command Substitution的一个例子,它只是倾向于删除尾随的换行符,而不是保持它们。。 “不过,但我曾经认为文件名中所有小写字母的压倒性使用有点奇怪/不必要,但是前几天我在想它实际上是一个非常舒适的系统。看到(更深入地)某天命令替换行为的韵律和原因……
Peter.O 2011年

1

我遇到了同样的问题,并发现以下示例。似乎引用对情况有所帮助,至少对我而言是这样:

http://tldp.org/LDP/abs/html/commandsub.html

dir_listing=`ls -l`
echo $dir_listing     # unquoted

# Expecting a nicely ordered directory listing.

# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# The newlines disappeared.


echo "$dir_listing"   # quoted
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh

2
这是这里发生的事情。假设您有一个$str包含字符串的变量"a b c"。如果传递$strecho不带引号(echo $str),echo将接收ab以及c作为三个独立的参数。您的shell不在乎参数之间的空格。echo然后将这些参数打印出来,并用空格将每个参数隔开,这样您就得到了"a b c"。如果将变量传递给echo带有引号(echo "$str")的变量,则外壳程序会将其视为一个参数并按echo原样接收它,因此空格不会折叠。换行符也是如此。

1
原始发布者遇到的问题是他命令的尾随换行符(仅是最后一行)消失了。除非传递了该-n标志,否则将在echo其输出中添加尾随换行符,因此示例中的两个回显都具有尾随换行符。但是,如果尝试使用echo -n $dir_listingecho -n "$dir_listing",则将看不到任何结尾的换行符,无论是否用引号引起来$dir_listing,这表明问题出在命令替换上。

2
tldp是学习shell脚本的一个非常过时的地方。请尝试使用Wooledge Bash Wiki。mywiki.wooledge.org/BashGuide
通配符

-1

为什么echo "hello "(不加引号)占用空间?外壳将IFS字符的值视为定界符,因此,尾随的(不定界任何字符)被删除。推荐替换只是在子外壳中简单执行推荐,因此遵循相同的逻辑。


@Philomath:无论x="hello  "x="hello     "将失去所有,如果通过显示其尾部的空格echo $x...但是只有最后命令Substituton的换行符丢失。
Peter.O 2011年

奇怪,我的重击没有什么区别(用xxd验证)
Philomath

我只是检查一下...它确实删除了所有尾随的换行符...当我得到该印象时,我正在尝试IFS,因此让我们取消一个:) ..但问题仍然存在...这是一个基本部分单词拆分可将多个空格压缩为一个空格,并完全删除前导和尾随空格。.我可以理解这一点,但是我当然不明白为什么使用Command Substitution 只能剥离\ n而不是全部空格(如和单词拆分),为什么只尾部呢?。最重要的是:“命令替换”仅部分替换了。为什么?
Peter.O 2011年

4
IFS与命令替换末尾的换行符无关。
吉尔(Gilles)'所以
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.