Shell 脚本读取缺少最后一行

2024-04-22

我的 bash shell 脚本有一个奇怪的问题,我希望能对此有所了解。

我的团队正在开发一个脚本,该脚本会迭代文件中的行并检查每一行中的内容。我们遇到了一个错误,当通过将不同脚本排序在一起的自动化流程运行时,看不到最后一行。

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

cat "$DATAFILE" | while read line 

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

我们更新了代码以使用以下代码迭代各行,问题就解决了:

for line in `cat "$DATAFILE"` 

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

我的问题分为两部分......为什么原始代码看不到最后一行,为什么这个改变会产生影响?

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

  • 之前写入文件的进程依赖进程结束来关闭文件描述符。
  • 问题脚本启动并打开文件的速度足够快,虽然前一个进程已经“结束”,但它还没有足够“关闭/清理”,以便系统自动关闭文件描述符。

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

任何对这些问题的见解,尤其是第一个问题,将不胜感激。


C 标准规定文本文件必须以换行符结尾,否则最后一个换行符后面的数据可能无法正确读取。

ISO/IEC 9899:2011 §7.21.2 流

文本流是由行组成的有序字符序列,每行 由零个或多个字符加上一个终止换行符组成。是否 最后一行需要一个终止换行符是实现定义的。人物 可能必须在输入和输出上添加、更改或删除,以符合不同的要求 在主机环境中表示文本的约定。因此,不必存在一对一的关系 流中的字符与外部字符之间的一一对应关系 表示。从文本流中读取的数据必然与数据相同 仅在满足以下条件时才先前写出到该流: 数据仅包含打印 字符和控制字符水平制表符和换行符;没有换行符 紧接着空格字符;最后一个字符是换行符。 是否在换行符之前写出空格字符 当读入是实现定义时出现。

我没想到文件末尾缺少换行符会导致出现问题bash(或任何 Unix shell),但这似乎是可重现的问题($ 是此输出中的提示):

$ echo xxx\\c
xxx$ { echo abc; echo def; echo ghi; echo xxx\\c; } > y
$ cat y
abc
def
ghi
xxx$
$ while read line; do echo $line; done < y
abc
def
ghi
$ bash -c 'while read line; do echo $line; done < y'
abc
def
ghi
$ ksh -c 'while read line; do echo $line; done < y'
abc
def
ghi
$ zsh -c 'while read line; do echo $line; done < y'
abc
def
ghi
$ for line in $(<y); do echo $line; done      # Preferred notation in bash
abc
def
ghi
xxx
$ for line in $(cat y); do echo $line; done   # UUOC Award pending
abc
def
ghi
xxx
$

它也不仅限于bash— 科恩壳 (ksh) and zsh也这样做。我生活,我学习;感谢您提出问题。

如上面的代码所示,cat命令读取整个文件。这for line in `cat $DATAFILE` 技术收集所有输出并用单个空格替换任意的空格序列(我得出的结论是文件中的每一行都不包含空格)。

在 Mac OS X 10.7.5 上测试。


POSIX 说什么?

POSIXread http://pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html命令规范说:

读取实用程序应从标准输入读取一行。

默认情况下,除非-r指定选项后, 将充当转义字符。未转义的 应保留后续字符的文字值,但 除外。如果 跟在 后面,则读取实用程序应将其解释为行继续。 和<newline>应在将输入拆分为字段之前删除。将输入拆分为字段后,应删除所有其他未转义的 字符。

如果标准输入是终端设备并且调用 shell 是交互式的,则当读取以 结尾的输入行时,read 将提示输入续行,除非-r选项已指定。

终止 (if any)应从输入中删除,并且结果应拆分为字段,如 shell 中参数扩展结果一样(请参阅字段拆分); [...]

请注意“(如果有)”(强调在引号中添加)!在我看来,如果没有换行符,它仍然应该读取结果。另一方面,它也说:

STDIN

标准输入应为文本文件。

然后你又回到关于不以换行符结尾的文件是否是文本文件的争论。

然而,同一页上的理由如下:

尽管标准输入必须是文本文件,因此始终以 结尾(除非它是空文件),但当-r不使用选项可能会导致输入不以 结尾。如果输入文件的最后一行以 结尾,则会发生这种情况。正是由于这个原因,在描述中的“应从输入中删除终止(如果有)”中使用了“如果有”。这并不是放松对标准输入是文本文件的要求。

这个基本原理必然意味着文本文件应该以换行符结尾。

文本文件的 POSIX 定义是:

3.395 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_395文本文件

包含组织成零行或多行的字符的文件。这些行不包含 NUL 字符,且长度不能超过 {LINE_MAX} 个字节,包括 字符。尽管 POSIX.1-2008 不区分文本文件和二进制文件(请参阅 ISO C 标准),但许多实用程序在操作文本文件时仅产生可预测或有意义的输出。具有此类限制的标准实用程序始终在其 STDIN 或 INPUT FILES 部分中指定“文本文件”。

这并没有直接规定“以 结尾”,但确实遵循 C 标准,并且它确实表示“包含组织成零个或多个字符的文件lines当我们查看“Line”的 POSIX 定义时,它说:

3.206 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206 Line

零个或多个非 字符加上一个序列 终止 字符。

因此,根据 POSIX 定义,文件必须以终止换行符结尾,因为它由行组成,并且每行必须以终止换行符结尾。


“无终端换行符”问题的解决方案

Note 戈登·戴维森 https://stackoverflow.com/users/89817/gordon-davisson's answer https://stackoverflow.com/questions/12916352/shell-script-read-missing-last-line/12919766#12919766。一个简单的测试表明他的观察是准确的:

$ while read line; do echo $line; done < y; echo $line
abc
def
ghi
xxx
$

因此,他的技术是:

while read line || [ -n "$line" ]; do echo $line; done < y

or:

cat y | while read line || [ -n "$line" ]; do echo $line; done

适用于末尾没有换行符的文件(至少在我的机器上)。


我仍然惊讶地发现 shell 删除了输入的最后一段(它不能被称为一行,因为它不以换行符结尾),但 POSIX 中可能有足够的理由这样做。显然,最好确保您的文本文件确实是以换行符结尾的文本文件。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Shell 脚本读取缺少最后一行 的相关文章

随机推荐

  • 如何在多个表中使用相同的 REPORT_COUNT 并递增它?

    我正在使用 iReport 5 6 为我的 JavaApp 生成报告 我有三个数据集 如下所示 数据集1查询 SELECT FROM players s WHERE s role 1 数据集2查询 SELECT FROM players s
  • Git 在两个远程之间同步

    我正在使用如下基础设施 我的笔记本电脑 工作站 B 和服务器 B 都在我的工作中 现在为了允许在家工作 我希望能够在工作站 A 上工作 我的笔记本电脑是唯一可以同时连接到两台服务器的设备 因为我在家里设置了 VPN 在当前的项目中 我们正在
  • SQL查找表中第一次出现的数据集

    假设我有一张桌子 CREATE TABLE T TableDTM TIMESTAMP NOT NULL Code INT NOT NULL 我插入一些行 INSERT INTO T TableDTM Code VALUES 2011 01
  • 每次包含同一个文件时都会对其进行标记吗?

    这个问题是关于PHP解析引擎的 当我在单个运行时中多次包含一个文件时 PHP 是否每次都会对其进行标记 或者是否保留缓存并仅在后续包含时运行已编译的代码 EDIT 更多细节 我没有使用外部缓存机制 我正在处理same文件被多次包含在同一个请
  • 如何加载使用 VaryByControl OutputCache 的控件,并指定属性值

    我有一个应该使用缓存的用户控件 其中VaryByControl The ascx文件看起来像这样 p Nothing p The TestControl代码隐藏文件中的类有一个int Test 财产和Page Load 填充的事件处理程序S
  • 线程 1:信号 SIGABRT

    当我为 Xcode 项目运行模拟时出现上述错误 这出现在 O abort with payload 文件中 dyld abort with payload 0x10aae66f8 lt 0 gt movl 0x2000209 eax imm
  • 为什么使用“==”或“is”比较字符串有时会产生不同的结果?

    两个字符串变量设置为相同的值 s1 s2总是返回True but s1 is s2有时返回False 如果我打开 Python 解释器并执行相同的操作is对比一下 成功了 gt gt gt s1 text gt gt gt s2 text
  • Python默认继承?

    如果我在Python中定义一个类 例如 class AClass slots a b c 它继承自哪个类 貌似没有继承object 如果定义一个类并且不声明任何特定的父类 则该类将成为 经典类 其行为与从对象继承的 新式类 略有不同 请参阅
  • 半素数分解的最佳算法是什么?

    半素数分解的最佳算法是什么 它可能与多重保理不同 对半素数进行因式分解的方式与对任何合数进行因式分解的方式相同 使用试除法和 pollard rho 来获得容易实现的目标 如果有的话 然后 p 1 p 1 和椭圆曲线 直到您确信因子太大 用
  • 仅西里尔文输入文本形式

    如何将输入文本限制为仅在 Web 表单文本框中输入Cyrillic人物 首先 您应该对页面和脚本使用支持西里尔字符 例如 UTF 8 的编码 然后 您可以使用正则表达式来检查 input keyup function this value
  • 为什么 ASP.NET DropDownList 控件需要单击两次才能在 Internet Explorer 中展开

    我有一个 ASP NET DropDownList 控件 它呈现到页面上的下拉列表 选择 HTML 标记 中 由于某种原因 当我使用 Internet Explorer 时 需要单击两次才能打开它并查看选项 这对最终用户来说只是额外的单击
  • 为什么使用成员数组而不是常量表达式调用 constexpr 函数?

    我有以下辅助功能 template
  • Codeigniter htaccess 和 base_url

    我安装了 codeigniter 并开始在上面编写一些代码 首先我想删除 index php 并对其进行一些研究 我用下面的一个小 htaccess 代码将其删除 RewriteEngine on RewriteCond 1 index p
  • link_to_remote 是否有等效的 :disable_with ?

    我有一个 link to remote 我想确保人们在等待它返回时只能单击它一次 有没有什么好的方法可以在有人点击后禁用它 更改链接的文本也很好 但我也想禁用它以确保 顺便说一句 这是 Ruby on Rails 您无法禁用链接 但您可以更
  • 在文件夹的上下文菜单中运行批处理文件

    当我右键单击任何文件夹时 我想运行此批处理文件 setenv cmd echo off SET CPLUS INCLUDE PATH C mingw32 include C mingw32 lib gcc mingw32 4 9 2 inc
  • 为什么create系统调用叫creat? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • RestKit 0.20.0pre5:每个请求的 HTTP 标头

    我想发送一个If Modified Since带有 GET 请求的 http 标头 由 RKObjectManager getObjectsAtPath the 迁移指南 https github com RestKit RestKit w
  • Maven 错误:也许您正在 JRE 而不是 JDK 上运行?

    我以前从未使用过 Maven 我正在按照说明进行操作here http ecmarchitect com alfresco developer series tutorials maven sdk tutorial tutorial htm
  • 使用类型类将 Haskell 中的值与类型关联起来

    我想使用类型类返回String功能上依赖于 Haskell 类型的实例 例如 假设我们有这样的类型Form 我想将字符串 form 与此类型相关联 给定类型Invocation 我想关联字符串 job 等等 重要的是我通常不会有相关类型的实
  • Shell 脚本读取缺少最后一行

    我的 bash shell 脚本有一个奇怪的问题 我希望能对此有所了解 我的团队正在开发一个脚本 该脚本会迭代文件中的行并检查每一行中的内容 我们遇到了一个错误 当通过将不同脚本排序在一起的自动化流程运行时 看不到最后一行 用于迭代文件中的