Shell脚本读取最后一行丢失

时间:2012-10-16 13:58:08

标签: bash process

我有一个... bash shell脚本的奇怪问题,我希望得到一些见解。

我的团队正在编写一个脚本,该脚本遍历文件中的行并检查每个行中的内容。我们遇到了一个错误,当通过将不同脚本排序在一起的自动化过程运行时,最后一行没有被看到。

用于迭代文件中的行的代码(存储在DATAFILE中的名称是

cat "$DATAFILE" | while read line 

我们可以从命令行运行脚本,它会看到文件中的每一行,包括最后一行,就好了。但是,当由自动化进程运行时(运行在相关脚本之前生成DATAFILE的脚本),最后一行从未见过。

我们更新了代码,使用以下代码迭代这些代码,并清除了问题:

for line in `cat "$DATAFILE"` 

注意:DATAFILE没有在文件末尾写入新行。

我的问题是两部分......为什么最后一行不会被原始代码看到,为什么这会改变有所作为呢?

我只想到我能想出为什么最后一行不会被看到:

  • 上一个写入文件的进程依赖于进程结束以关闭文件描述符。
  • 问题脚本正在启动并以足够快的速度打开文件,而前一个进程已经“结束”,它没有“关闭/清理”到足以让系统自动关闭文件描述符

话虽如此,似乎如果你在shell脚本中有2个命令,那么第一个应该在脚本运行第二个命令时完全关闭。

对这些问题的任何见解,特别是第一个,都将非常感激。

6 个答案:

答案 0 :(得分:36)

根据POSIX spec for the read command,如果“检测到文件结尾或发生错误”,它应返回非零状态。由于EOF在读取最后一个“行”时被检测到,因此它设置$line然后返回错误状态,并且错误状态阻止循环在最后一行“行”上执行。解决方案很简单:如果读取命令成功,则执行循环;如果有任何内容被读入$line,则执行循环。

while read line || [ -n "$line" ]; do

答案 1 :(得分:12)

添加一些其他信息:

  1. 没有必要在while循环中使用catwhile ...;do something;done<file就够了。
  2. Don't read lines with for.
  3. 使用while循环读取行时:

    1. 正确设置IFS(否则可能会丢失缩进)。
    2. You should almost always use the -r option with read.
    3. 满足上述要求时,正确的while循环将如下所示:

      while IFS= read -r line; do
        ...
      done <file
      

      并使其能够使用最终没有换行的文件(从here重新发布我的解决方案):

      while IFS= read -r line || [ -n "$line" ]; do
        echo "$line"
      done <file
      

      或者使用grep和while循环:

      while IFS= read -r line; do
        echo "$line"
      done < <(grep "" file)
      

答案 2 :(得分:1)

使用sed匹配文件的最后一行,如果一个文件不存在,它将附加一个换行符,并让它对该文件进行内联替换:

sed -i '' -e '$a\' file

代码来自此stackexchange link

注意:我已将空单引号添加到-i '',因为至少在OS X中,-i使用-e作为备份文件的文件扩展名。我很乐意评论原帖,但缺少50分。也许这会让我在这个帖子中获得一些,谢谢。

答案 3 :(得分:0)

我在命令行中对此进行了测试

# create dummy file. last line doesn't end with newline
printf "%i\n%i\nNo-newline-here" >testing

使用您的第一个表单(管道到while循环)进行测试

cat testing | while read line; do echo $line; done

这会错过最后一行,这是有道理的,因为read只会获得以换行符结尾的输入。


使用第二种形式(命令替换)进行测试

for line in `cat testbed1` ; do echo $line; done

这也是最后一行

<小时/> read只有在被换行符终止时才会输入,这就是你错过最后一行的原因。

另一方面,在第二种形式

`cat testing` 

扩展为

的形式
line1\nline2\n...lineM 

使用IFS将shell分隔成多个字段,所以你得到

line1 line2 line3 ... lineM 

这就是为什么你仍然得到最后一行。

p / s:我不明白你是如何得到第一张表格......

答案 4 :(得分:0)

作为一种解决方法,在从文本文件中读取之前,可以将换行符附加到文件中。

echo "\n" >> $file_path

这将确保读取文件中以前的所有行。

答案 5 :(得分:0)

我有类似的问题。 我正在做一个文件的猫,将它管道排序,然后将结果传递给'while read var1 var2 var3'。 即: cat $ FILE | sort -k3 | while read Count IP Name 做 “do”下的工作是一个if语句,用于识别$ Name字段中的更改数据,并根据更改或没有更改确定$ Count的总和或将总计行打印到报告中。 我也遇到了无法将最后一行打印到报告中的问题。 我选择了将cat / sort重定向到一个新文件的简单权宜之计,回显新文件的换行符,然后在新文件上运行我的“while read Count IP Name”并获得成功结果。 即: cat $ FILE | sort -k3&gt;新文件 echo“\ n”&gt;&gt;新文件 cat NEWFILE | while读取计数IP名称 做 有时简单,不优雅是最好的方式。