如何使bash反引号运算符在输出中保留换行符?


36

有没有办法让bash在反引号替换中不吃换行符?

例如:

var=`echo line one && echo line two`
echo $var

line one line two

我想要的是

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two

Answers:


54

反引号替换不是问题,而是echo; 您必须引用该变量以使控制字符起作用:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two

那么,bash在命令留置位上扩展变量时会删除换行符吗?可以通过引用变量来禁用该行为吗?
slowpoison 2012年

7
bash执行命令,引用之前执行参数扩张有必要通知bash要打印一个字符串并且不为4字(lineonelinetwo)。尝试:echo a <many spaces> becho "a <many spaces> b"。也看一下这个答案
cyrus 2012年

5

即使@cyrus是正确的-它实际上并不能回答整个问题,也没有任何解释。

因此,让我们通过它。

字符串中的换行符

首先确定期望的字节序列:

$ { echo a; echo b; } | xxd
0000000: 610a 620a                                a.b.

现在,使用命令替换(第3.5.4节)尝试捕获此字节序列:

$ x=$( echo a; echo b; )

然后,执行简单的回显以验证字节序列:

$ echo $x | xxd
0000000: 6120 620a                                a b.

因此,似乎第一个换行符被替换为空格,而第二个换行符则保持不变。但为什么 ?

让我们看一下这里实际发生的情况:

首先,bash将进行Shell参数扩展(第3.5.3节)

“ $”字符引入参数扩展,命令替换或算术扩展。可以将要扩展的参数名称或符号括在括号中,该括号是可选的,但用于保护要扩展的变量免受紧随其后的字符的影响,这些字符可以解释为名称的一部分。

然后bash将进行单词拆分(第3.5.7节)

Shell会扫描双引号中未出现的参数扩展,命令替换和算术扩展的结果,以进行单词拆分。

Shell将$ IFS的每个字符视为定界符,并将其他扩展的结果拆分为这些字符上的单词。如果未设置IFS或它的值正好是...

接下来,bash将其视为简单命令(第3.2.1节)

简单命令是最常遇到的命令。它只是一个由空格分隔的单词序列,由外壳程序的一个控制运算符终止(请参见定义)。通常,第一个单词指定要执行的命令,其余单词是该命令的参数。

空白的定义(第2部分-定义)

空白空格或制表符。

最后,bash调用echo(4.2节-Bash内置命令)内部命令

...输出以空格分隔并以换行符终止的args。...

因此,总而言之,通过单词拆分除去了换行符,然后echo得到2个args,“ a”和“ b”,然后将它们输出,以空格分隔并以换行符结尾。

进行@cyrus建议的操作(并使用-n抑制换行符回显),效果会更好:

$ echo -n "$x" | xxd
0000000: 610a 62                                  a.b

字符串末尾的换行符

尽管它仍然不完美,但尾随的换行符已消失。仔细查看命令替换(第3.5.4节)

Bash通过执行命令并将命令替换替换为命令的标准输出来执行扩展,并删除所有尾随的换行符。

既然清楚了换行符消失的原因,bash可能会被欺骗以保留它。为此,请在末尾添加一个附加字符串,并在使用变量时将其删除:

$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a                                a.b.

TL; DR

在输出末尾添加多余的部分,并引用变量:

$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$ 
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$ 
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46

0

我会在对cYrus的答案中添加我的一小部分评论,但我仍无法对我认为的答案发表评论。因此,我将为自己提供完整的cYrus答案。

在代码的第一行中发生的事情是,事实上,反引号替换实际上吃掉了替换命令输出的尾随换行符,如记录所示。但是它不会吃掉两行之间的换行符。

在第二行代码中发生的事情是,回显使用两个参数(第一行和第二行)执行。引用扩展变量将解决以下问题:echo "$var"这将保留第一行和第二行之间的换行符。并且echo默认情况下会添加尾随换行符,以便一切正常。

现在,如果您想在命令替换的输出中保留所有结尾的换行符,一种解决方法是在输出中添加另一行,您可以在命令替换后将其删除,请参见此处

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.