Bash读取忽略前导空格

Lah*_*ima 29 bash

我有a.txt以下内容的文件

    aaa
    bbb
Run Code Online (Sandbox Code Playgroud)

当我执行以下脚本时:

while read line
do
    echo $line
done < a.txt > b.txt
Run Code Online (Sandbox Code Playgroud)

生成b.txt包含以下内容

aaa
bbb
Run Code Online (Sandbox Code Playgroud)

可以看出,线条的前导空间已被删除.我怎样才能保留领先的空间?

Eta*_*ner 40

关于逐行读取数据的Bash FAQ条目对此进行了介绍.

read命令修改每行读取; 默认情况下,它会删除所有前导和尾随空白字符(空格和制表符,或IFS中存在的任何空格字符).如果不需要,则必须清除IFS变量:

# Exact lines, no trimming
while IFS= read -r line; do
  printf '%s\n' "$line"
done < "$file"
Run Code Online (Sandbox Code Playgroud)

正如查尔斯达菲正确地指出的那样(并且我因为关注这个IFS问题而错过了); 如果你想在输出中看到空格,你还需要在使用它时引用变量,否则shell将再次删除空格.

关于引用代码段与原始代码相比的其他一些差异的注释.

-r参数的使用read包含在先前链接页面顶部的单个句子中.

读取的-r选项可防止反斜杠解释(通常用作反斜杠换行符对,以继续多行).如果没有此选项,输入中的任何反斜杠都将被丢弃.您应该几乎总是将-r选项与read一起使用.

至于使用printf而不是echo那里的行为echo,有点不幸的是,在所有环境中并不是可移植的,并且差异可能很难处理.printf另一方面,它是一致的,可以完全稳健地使用.

  • 如果你不给`read`任何用来保存输入的参数(依赖于默认变量`REPLY`),就不会删除空格,你可以省略对`IFS`的修改.也就是说,`读取-r; 做printf'%s \n'"$ REPLY"; 完成<"$ file"` (5认同)
  • @chepner:`man bash`说`$ REPLY`(强调我的):"当没有提供参数时,设置为read builtin命令读取的_line_." 因此,想法是将_whole行读为is_,而不是将_splitting为fields_.然而,反直觉的是你还必须指定`-r`以避免反斜杠解释.请注意,(很少使用)`select`构造 - 在哪里分割成字段甚至不进入图片 - 也将`$ REPLY`设置为用户输入的任何内容(_invariably_反斜杠 - 解释,但也按原样). (3认同)
  • 我不确定; 据我所知,它似乎没有记录在案.如果您将其视为零参数需要将该行拆分为零字段,这是有道理的,这意味着"IFS"没有用处.(这假设你接受将一条线分成一个区域仍然是一个分裂,虽然是一个退化的.)无论如何,它是一个"bash"主义; POSIX`read`至少需要一个参数. (2认同)
  • 至于为什么`-r`仍然需要抑制反斜杠解释,即使只使用`$ REPLY`(没有指定任何变量名):不指定`-r`可能一次读取_multiple_行(没有换行符连接),如果输入有`\`-escaped换行符; 如果仅使用`$ REPLY` _implied_` -r`,则此多行行为将不可用. (2认同)
  • `read`的默认行为允许_line continuation_通过用`\`结束一行并在下一行继续它('\`和换行都是_discarded_).这很少有用,除了_interactive_输入,并且,由于_any``\<char>`对中的`\`被丢弃,通常会让用户感到惊讶.我认为这里真正的问题是`-r`(_always_ read _only one_ line,保留所有反斜杠)的行为应该一直是_default_行为,但是当前的行为是POSIX规定的,所以我们坚持它.(续下评论) (2认同)
  • (从上一个评论开始)结果是:正如你的引用所述,你几乎总是想要使用`-r`,而我并不认为真正需要用`-r`来支持行继续.虽然不完全相同,如果你确实需要在`bash`,`ksh`和`zsh`中读取行,你可以使用`read -rd <delimChar> ...`,它的优点是换行不必(也不应该)`\`-escaped. (2认同)

Cha*_*ffy 14

这里有几个问题:

  • 除非IFS被清除,否则read剥离前导和尾随空格.
  • echo $linestring-splits和glob-expands的内容$line,将其分解为单个单词,并将这些单词作为单独的参数传递给echo命令.因此,即使IFS在read时间被清除,echo $line仍然会丢弃前导和尾随空格,并将单词之间的空格更改为单个空格字符.此外,仅包含该字符的*行将被展开以包含文件名列表.
  • echo "$line"是一个重大的改进,但仍然不能正确处理诸如-n它本身作为echo参数的值.printf '%s\n' "$line"会完全解决这个问题.
  • read-r将反斜杠视为连续字符而不是文字内容,这样它们就不会包含在生成的值中,除非加倍自行转义.

从而:

while IFS= read -r line; do
  printf '%s\n' "$line"
done
Run Code Online (Sandbox Code Playgroud)

  • 描述没有`-r`的`read`行为的另一种方式:输入的解析方式与(POSIX)shell本身解析具有单独`\`-escaped字符的裸字的方式相同(例如,作为参数列表),如http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01所述,并且基本上在http://pubs.opengroup.org/的`read`的POSIX规范中重复onlinepubs/9699919799 /公共事业/ read.html. (3认同)
  • 谢谢 - 我想要查看源材料,以确定如何最好地修改我的答案的那一部分. (2认同)