Bash 读取忽略前导空格
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29689172/
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
Bash read ignores leading spaces
提问by Lahiru Chandima
I have file a.txt
with following content
我有a.txt
以下内容的文件
aaa
bbb
When I execute following script:
当我执行以下脚本时:
while read line
do
echo $line
done < a.txt > b.txt
generated b.txt
contains following
生成b.txt
包含以下内容
aaa
bbb
It is seen that the leading spaces of lines have got removed. How can I preserve leading spaces?
可以看出,行的前导空格已被删除。如何保留前导空格?
回答by Etan Reisner
This is covered in the Bash FAQ entry on reading data line-by-line.
关于逐行读取数据的 Bash FAQ 条目中介绍了这一点。
The read command modifies each line read; by default it removes all leading and trailing whitespace characters (spaces and tabs, or any whitespace characters present in IFS). If that is not desired, the IFS variable has to be cleared:
# Exact lines, no trimming while IFS= read -r line; do printf '%s\n' "$line" done < "$file"
读取命令修改读取的每一行;默认情况下,它会删除所有前导和尾随空白字符(空格和制表符,或 IFS 中存在的任何空白字符)。如果不需要,则必须清除 IFS 变量:
# Exact lines, no trimming while IFS= read -r line; do printf '%s\n' "$line" done < "$file"
As Charles Duffy correctly points out (and I'd missed by focusing on the IFS
issue); if you want to see the spaces in your output you also need to quote the variable when you use it or the shell will, once again, drop the whitespace.
正如查尔斯·达菲 (Charles Duffy) 正确指出的那样(我因关注这个IFS
问题而错过了);如果您想查看输出中的空格,您还需要在使用变量时引用它,否则 shell 将再次删除空格。
Notes about some of the other differences in that quoted snippet as compared to your original code.
注意与原始代码相比,引用的代码段中的一些其他差异。
The use of the -r
argument to read
is covered in a single sentence at the top of the previously linked page.
-r
参数 to的使用read
包含在先前链接页面顶部的单个句子中。
The -r option to read prevents backslash interpretation (usually used as a backslash newline pair, to continue over multiple lines). Without this option, any backslashes in the input will be discarded. You should almost always use the -r option with read.
read 的 -r 选项可防止反斜杠解释(通常用作反斜杠换行符对,以在多行上继续)。如果没有这个选项,输入中的任何反斜杠都将被丢弃。您几乎应该总是将 -r 选项与 read 一起使用。
As to using printf
instead of echo
there the behavior of echo
is, somewhat unfortunately, not portably consistent across all environments and the differences can be awkward to deal with. printf
on the other hand is consistent and can be used entirely robustly.
至于使用printf
而不是echo
那里的行为echo
,有点不幸的是,在所有环境中都不是可移植的,并且差异可能难以处理。printf
另一方面是一致的,可以完全稳健地使用。
回答by Charles Duffy
There are several problems here:
这里有几个问题:
- Unless
IFS
is cleared,read
strips leading and trailing whitespace. echo $line
string-splits and glob-expands the contents of$line
, breaking it up into individual words, and passing those words as individual arguments to theecho
command. Thus, even with IFS cleared atread
time,echo $line
would still discard leading and trailing whitespace, and change runs of whitespace between words into a single space character each. Additionally, a line containing only the character*
would be expanded to contain a list of filenames.echo "$line"
is a significant improvement, but still won't correctly handle values such as-n
, which it treats as an echo argument itself.printf '%s\n' "$line"
would fix this fully.read
without-r
treats backslashes as continuation characters rather than literal content, such that they won't be included in the values produced unless doubled-up to escape themselves.
- 除非
IFS
被清除,否则read
去除前导和尾随空格。 echo $line
string-splits 和 glob-expands 的内容$line
,将其分解为单独的单词,并将这些单词作为单独的参数传递给echo
命令。因此,即使 IFS 被read
及时清除,echo $line
仍然会丢弃前导和尾随空格,并将单词之间的空格更改为每个空格字符。此外,仅包含字符的*
行将被扩展为包含文件名列表。echo "$line"
是一项重大改进,但仍无法正确处理诸如 之类的值-n
,它会将其视为 echo 参数本身。printf '%s\n' "$line"
将完全解决这个问题。read
without-r
将反斜杠视为连续字符而不是文字内容,这样它们就不会包含在生成的值中,除非加倍以逃避自身。
Thus:
因此:
while IFS= read -r line; do
printf '%s\n' "$line"
done