85

为了加快一些 bash 脚本的执行,我想使用命令替换将命令的结果保存在变量中,但是命令替换用空格替换0x0A换行符。例如:

a=`df -H`

或者

a=$( df -H )

当我想进一步处理$a时,换行符被空格替换,所有行现在都在一行上,这更难 grep:

echo $a

避免命令替换删除换行符的简单技巧是什么?

4

3 回答 3

136

不删除非尾随换行符

您正在寻找的换行符在那里,您只是看不到它们,因为您使用echo时没有引用变量。

验证

$ a=$( df -H )
$ echo $a
Filesystem Size Used Avail Use% Mounted on /dev/sda3 276G 50G 213G 19% / udev 2.1G 4.1k 2.1G 1% /dev tmpfs 832M 820k 832M 1% /run none 5.3M 0 5.3M 0% /run/lock none 2.1G 320k 2.1G 1% /run/shm
$ echo "$a"
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda3       276G   50G  213G  19% /
udev            2.1G  4.1k  2.1G   1% /dev
tmpfs           832M  820k  832M   1% /run
none            5.3M     0  5.3M   0% /run/lock
none            2.1G  320k  2.1G   1% /run/shm
$ 

尾随换行符被删除

正如@user4815162342正确指出的那样,虽然输出中的换行符没有被删除,但尾随换行符会通过命令替换被删除。参见下面的实验:

$ a=$'test\n\n'
$ echo "$a"
test


$ b=$(echo "$a")
$ echo "$b"
test
$

在大多数情况下,这无关紧要,因为echo将添加已删除的换行符(除非使用-n选项调用它),但在某些极端情况下,程序输出中的尾随换行符不止一个,它们对于一些理由。

解决方法

1.添加虚拟角色

在这种情况下,正如@Scrutinizer 所提到的,您可以使用以下解决方法:

$ a=$(printf 'test\n\n'; printf x); a=${a%x}
$ echo "$a"
test


$ 

说明:字符x被添加到输出中(使用printf x),在换行符之后。由于换行符不再尾随,因此它们不会被命令替换删除。下一步是删除x我们添加的,使用%运算符 in${a%x}。现在我们有了原始输出,所有换行符都存在!!!

2. 使用进程替换读取

代替使用命令替换将程序的输出分配给变量,我们可以使用进程替换 将程序的输出提供给read内置命令(归功于@ormaaj)。进程替换保留所有换行符。将输出读取到变量有点棘手,但您可以这样做:

$ IFS= read -rd '' var < <( printf 'test\n\n' ) 
$ echo "$var"
test


$ 

解释:

  • 我们将读取命令的内部字段分隔符IFS=设置为 null,使用. 否则read不会将整个输出分配给var,而只会分配第一个标记。
  • read我们使用 options调用-rd ''。这r是为了防止反斜杠充当特殊字符,并将d ''分隔符设置为空,以便 read 读取整个输出,而不仅仅是第一行。

3. 从管道中读取

代替使用命令或进程替换将程序的输出分配给变量,我们可以将程序的输出通过管道传递给read命令(归功于@ormaaj)。管道还保留所有换行符。但是请注意,这次我们lastpipe使用内置shopt. 这是必需的,以便read在当前 shell 环境中执行命令。否则,该变量将在子 shell 中分配,并且无法从脚本的其余部分访问。

$ cat test.sh 
#!/bin/bash
shopt -s lastpipe
printf "test\n\n" | IFS= read -rd '' var
echo "$var"
$ ./test.sh 
test


$
于 2013-03-03T09:46:04.873 回答
1

我试图解决这个问题,因为我正在使用 bash 流式传输在 F# 脚本上运行解释器的结果。经过一些试验和错误,这原来解决了这个问题:

$ cat fsi.ch
#!/bin/bash
echo "$(fsharpi --quiet --exec --nologo $1)"

$ fsi.ch messages.fsx
Welcome to my program. Choose from the menu:
new | show | remove

当然,假设您需要运行一个终端程序。希望这可以帮助。

于 2016-07-10T21:31:12.580 回答
1

另一个“巧妙的技巧”是使用回车符,它可以防止换行符被剥离,但不会向输出添加任何内容:

$ my_func_1 () {
>     echo "This newline is squashed"
> }
$ my_func_2 () {
>     echo "This newline is not squashed"
>     echo -n $'\r'
> }
$ echo -n "$(my_func_1)" && echo -n "$(my_func_2)" && echo done
This newline is squashedThis newline is not squashed
done
$

但是买家要注意:正如评论中提到的,这对于只是进入终端的输出可以很好地工作,但如果你将它传递给另一个进程,你可能会混淆它,因为它可能不会期待奇怪的 terminating '\r'

于 2020-10-22T10:28:50.960 回答