如何避免 bash 命令替换以删除换行符?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15184358/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:24:53  来源:igfitidea点击:

How to avoid bash command substitution to remove the newline character?

bashnewlinecommand-substitution

提问by Laurent

To speed up some bash script execution, I would like to keep the result of a command in a variable using command substitution, but the command substitution replaces the 0x0Anewline character by a space. For example:

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

a=`df -H`

or

或者

a=$( df -H )

When I want to process further $a, the newline characters are replaced by a space and all the lines are now on one line, which is much harder to grep:

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

echo $a

What would be the easy tricks to avoid the newline character being removed by the command substitution?

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

回答by user000001

Non-trailing newlines are not removed

不删除非尾随换行符

The newlines you are looking for are there, you just don't see them, because you use echowithout quoting the variable.

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

Validation:

验证

$ 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
$ 

Trailingnewlines are removed

删除尾随换行符

As @user4815162342correctly pointed out, although newlines within the output are not removed, trailing newlinesare removed with command substitution. See experiment below:

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

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


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

In most cases this does not matter, because echowill add the removed newline (unless it is invoked with the -noption), but there are some edge cases where there are more that one trailing newlines in the output of a program, and they are significant for some reason.

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

Workarounds

解决方法

1. Add dummy character

1.添加虚拟角色

In these case, as @Scrutinizermentioned, you can use the following workaround:

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

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


$ 

Explanation:Character xis added to the output (using printf x), after the newlines. Since the newlines are not trailingany more, they are not removed by the command substitution. The next step is to remove the xwe added, using the %operator in ${a%x}. Now we have the original output, with all newlines present!!!

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

2. Read using process substitution

2. 使用进程替换读取

Instead of using command substitution to assign the output of a program to variable, we can instead use process substitutionto feed the output of the program to the readbuilt-in command (credit to @ormaaj). Process substitution preserves all newlines. Reading the output to a variable is a bit tricky, but you can do it like this:

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

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


$ 

Explanation:

解释:

  • We set the internal field separatorfor the read command to null, with IFS=. Otherwise readwould not assign the entire output to var, but only the first token.
  • We invoke readwith options -rd ''. The ris for preventing the backslash to act as a special character, and with d ''set the delimiter to nothing, so that read reads the entire output, instead of just the first line.
  • 我们将读取命令的内部字段分隔符设置为 null,使用IFS=. 否则read不会将整个输出分配给var,而只会分配第一个标记。
  • 我们read用 options调用-rd ''。这r是为了防止反斜杠充当特殊字符,并将d ''分隔符设置为空,以便 read 读取整个输出,而不仅仅是第一行。

3. Read from a pipe

3. 从管道中读取

Instead of using command or process substitution to assign the output of a program to variable, we can instead pipe the output of the program to the readcommand (credit to @ormaaj). Piping also preserves all newlines. Note however, that this time we set the lastpipeshell optional behavior, using the shoptbuiltin. This is required, so that the readcommand is executed in the current shell environment. Otherwise, the variable will be assigned in a subshell, and it will not be accessible from the rest of the script.

我们可以代替使用命令或进程替换将程序的输出分配给变量,而是将程序的输出通过管道传递给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


$

回答by ワイきんぐ

I was trying to wrap my head around this because I was using bash to stream in the result of running the interpreter on an F# script. After some trial and error, this turned out to solve the problem:

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

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

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

Assuming, of course that you need to run a terminal program. Hope this helps.

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