使用shell脚本中的read命令逐行读取输入文件会跳过最后一行

Cai*_*ife 20 bash shell file-io parsing

我通常使用read命令逐行读取shell脚本的输入文件.如果未在输入文件blah.txt中的最后一行末尾插入新行,则下面的示例代码会产生错误的结果.

#!/bin/sh

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

因此,如果输入文件读取类似于 -

One 
Two
Three
Four
Run Code Online (Sandbox Code Playgroud)

我四点后没有回复,脚本无法读取最后一行,并打印出来

One
Two
Three
Run Code Online (Sandbox Code Playgroud)

现在,如果我在四个之后留下一个额外的空白行,比如,

One 
Two
Three
Four
//blank line
Run Code Online (Sandbox Code Playgroud)

输出打印所有行,包括四行.但是,当我使用cat命令读取一行时,情况并非如此; 包括最后一行在内的所有行都打印出来,而不必在末尾添加额外的空白行.

有人知道为什么会这样吗?我创建的脚本主要由其他人运行,因此没有必要在每个输入文件的末尾添加额外的空行.

我一直试图解决这个问题; 如果你有任何解决方案我会很感激(当然,cat命令是一个,但我想知道阅读不起作用的原因).

ric*_*ici 21

read读取直到找到换行符或文件结尾,并在遇到文件结尾时返回非零退出代码.所以它很可能同时读取一行并返回非零退出代码.

因此,如果输入可能未被换行终止,则以下代码不安全:

while read LINE; do
  # do something with LINE
done
Run Code Online (Sandbox Code Playgroud)

因为while不会在最后一行执行该主体.

从技术上讲,未以换行符终止的文件不是文本文件,文本工具可能会以奇怪的方式在这样的文件上失败.但是,我总是不愿意回避这个解释.

解决问题的一种方法是测试读取的内容是否为非空(-n):

while read -r LINE || [[ -n $LINE ]]; do
  # do something with LINE
done
Run Code Online (Sandbox Code Playgroud)

其他解决方案包括使用mapfile将文件读入数组,通过某些实用程序管理文件,保证正确终止最后一行(grep .例如,如果您不想处理空行),或进行迭代处理使用类似的工具awk(这通常是我的偏好).

注意内置-r几乎肯定需要read; 它导致read不重新解释\输入中的序列.


Jah*_*hid 5

像这样使用while循环:

while IFS= read -r line || [ -n "$line" ]; do
  echo "$line"
done <file
Run Code Online (Sandbox Code Playgroud)

或者使用grepwhile循环:

while IFS= read -r line; do
  echo "$line"
done < <(grep "" file)
Run Code Online (Sandbox Code Playgroud)

使用grep .代替grep ""将跳过空行。

注意:

  1. 使用IFS=可使所有行缩进保持不变。

  2. 您几乎应该始终将-r选项与read一起使用。

  3. 不要用 for

  4. 结尾没有换行符的文件不是标准的UNIX文本文件。