读取 -a 数组 -d '\n' < foo,退出代码 1

Ras*_*sWL 9 bash newlines read

如果我尝试执行

read -a fooArr -d '\n' < bar
Run Code Online (Sandbox Code Playgroud)

退出代码是 1——即使它完成了我想要的;将每一行放在bar数组的一个元素中fooArr(使用 bash 4.2.37)。

有人可以解释为什么会这样


我找到了其他方法来解决这个问题,比如下面的方法,所以这不是我要的。

for ((i=1;; i++)); do
    read "fooArr$i" || break;
done < bar
Run Code Online (Sandbox Code Playgroud)

或者

mapfile -t fooArr < bar
Run Code Online (Sandbox Code Playgroud)

ric*_*ici 16

需要解释的是该命令似乎有效,而不是它的退出代码

'\n'是两个字符:一个反斜杠\和一个字母n。你认为你需要的是$'\n',这是一个换行符(但这也不对,见下文)。

-d选项执行以下操作:

  -d delim  continue until the first character of DELIM is read, rather
            than newline
Run Code Online (Sandbox Code Playgroud)

因此,如果没有该选项,read将读取到换行符,使用 中的字符$IFS作为分隔符将行拆分为单词,然后将单词放入数组中。如果您指定-d $'\n',将行分隔符设置为换行符,它会做完全相同的事情。设置-d '\n'意味着它将读取到第一个反斜杠(但是,再一次,见下文),它是delim. 由于您的文件中没有反斜杠,因此read将在文件末尾终止,并且:

Exit Status:
The return code is zero, unless end-of-file is encountered, read times out,
or an invalid file descriptor is supplied as the argument to -u.
Run Code Online (Sandbox Code Playgroud)

所以这就是退出代码为 1 的原因。

从您认为该命令有效的事实中,我们可以得出结论,文件中没有空格,因此,read在徒劳地希望找到反斜杠的情况下读取整个文件后,会将其拆分为空格(默认值为$IFS),包括换行符。因此,每一行(或每个单词,如果一行包含多个单词)都被隐藏到数组中。

被盗反斜杠的神秘案例

现在,我怎么知道该文件不包含任何反斜杠?因为您没有将-r标志提供给read

  -r                do not allow backslashes to escape any characters
Run Code Online (Sandbox Code Playgroud)

因此,如果文件中有任何反斜杠,它们就会被删除,除非您连续有两个反斜杠。而且,当然,有证据表明read退出代码为 1,这表明它没有找到反斜杠,因此也没有连续两个。

外卖

如果几乎每个命令背后都没有隐藏的陷阱,Bash 就不会是 bash,read也不例外。这里有几个:

  1. 除非您指定-r,否则read将解释反斜杠转义序列。除非这实际上是您想要的(偶尔是,但只是偶尔),否则您应该记住指定-r避免在输入中有反斜杠的罕见情况下字符消失。

  2. 这一事实read返回的退出代码为1并不意味着失败。它很可能成功了,除了找到行终止符。所以要小心这样的循环:while read -r LINE; do something with LINE; done 因为do something在最后一行没有换行符的罕见情况下,最后一行会失败。

  3. read -r LINE 保留反斜杠,但不保留前导或尾随空格。

  • “最后一行没有换行符的罕见情况”在我看来并不是建议“永远不要使用‘while read’”的令人信服的理由。否则,很棒的答案。 (2认同)
  • 最后一行总是有一个换行符。这就是一行的定义方式:它以换行符结束。如果非空文件不以换行符结尾,则它不是文本文件,并且您不能使用诸如 shell 实用程序“read”之类的文本处理工具。 (2认同)
  • @glennjackman:我,我主要是用 awk 遍历列文件。对于迭代文件不太大的整行,`mapfile` 非常酷。对于快速和肮脏的 hack,我将使用禁止的 while 循环,但我已停止将其放入生产脚本中。YMMV 和我软化了答案中的警告。 (2认同)