Bash 如果最后一行是';使用read时,s未以新行字符(\n)结尾

Bash 如果最后一行是';使用read时,s未以新行字符(\n)结尾,bash,shell,built-in,Bash,Shell,Built In,有一段时间我注意到,read如果文件末尾没有“换行符”,就不会真正读取文件的最后一行。这是可以理解的,如果有人认为,只要文件中没有“换行”字符,它就好像包含0行(这是很难承认的)。例如,请参见以下内容: $ echo 'foo' > bar ; wc -l bar 1 bar 但是 $ echo -n 'bar' > foo ; wc -l foo 0 foo 问题是:当我使用read来处理我自己没有创建或修改过的文件,并且我不知道这些文件是否真的以“换行符”结尾时,我如何处理这

有一段时间我注意到,
read
如果文件末尾没有“换行符”,就不会真正读取文件的最后一行。这是可以理解的,如果有人认为,只要文件中没有“换行”字符,它就好像包含0行(这是很难承认的)。例如,请参见以下内容:

$ echo 'foo' > bar ; wc -l bar
1 bar
但是

$ echo -n 'bar' > foo ; wc -l foo
0 foo

问题是:当我使用
read
来处理我自己没有创建或修改过的文件,并且我不知道这些文件是否真的以“换行符”结尾时,我如何处理这种情况?

POSIX要求文件中的任何行在末尾有换行符来表示它是一行。但提供了一个解决方案,完全符合您所描述的场景。最终的产品是这个小碎块

newline='
'
lastline=$(tail -n 1 file; echo x); lastline=${lastline%x}
[ "${lastline#"${lastline%?}"}" != "$newline" ] && echo >> file
# Now file is sane; do our normal processing here...

如果必须使用read,请尝试以下操作:

awk '{ print $0}' foo | while read line; do
    echo the line is $line
done

由于awk似乎即使没有换行符也能识别行,这或多或少是迄今为止给出的答案的组合

它不会就地修改文件

(cat file; tail -c1 file | grep -qx . && echo) | while read line
do
    ...
done

read
实际上是将未终止的行读取到指定的var中(
$REPLY
默认情况下)。它在这样一行上也返回false,这只是表示“文件结束”;直接在经典的
循环中使用其返回值,而
循环跳过最后一行。如果稍微更改循环逻辑,则可以使用
读取
,正确处理非新行终止的文件,而无需事先清理:

while read -r || [[ -n "$REPLY" ]]; do
    # your processing of $REPLY here
done < "/path/to/file"
读取时-r | |[[-n“$REPLY”];做
#您对$REPLY的处理在此完成
完成
注意,这比依赖外部的解决方案快得多


改进循环逻辑的提示。

谢谢,这正是我想要的。这也完全没有必要,除非您明确希望清理此类文件
read
可以很好地处理它们,如中所示。我更愿意远离awk,但这仍然是一个好主意。谢谢这解释了使用
awk
的风险:“事实证明,由于awk处理输入的方式,无论原始文件是否正确,直接的oneliner
awk 1 file>tempfile&&mv tempfile file
都会生成正确的输出。但是,如果文件很大,我们希望避免只为了修复最后一行而读取整个文件(如果它是正确的,甚至不是那样的)。”此解决方案不需要您使用awk执行任何高级操作。事实上,如果您大量使用shell脚本,awk将是一个很好的工具。学习基础知识不需要很长时间。awk通常对文件处理非常有用。它可能比使用shell命令快得多,尤其是在文件较大的情况下。我认为这是最好的答案,因为用最后的换行符重写文件X并不总是每个人都可以选择的。此外,这段代码很容易理解(当脚本不应该是“只写”的时候,这总是一个好主意)和最短最安全的代码(见JakeGould的评论)。此oneliner还可以存储到单独的脚本或别名中,因此可重复使用。虽然我完全同意其基本原理,但我不同意该实现的价值。除了它使用完全不必要的外部量之外,管道内的循环也有。请参阅,以获取当前仅限shell的内部解决方案。实际上,
read
读取未终止的最后一行很好。问题在于在循环中使用其返回值–请参阅。只是想知道:这与读取-r
时的
cat文件有何不同?@Pumbaa80它不启动外部进程,这会使它更快,并且循环不会在子shell中执行,同样也更快。这不会导致不太有效,因为如果文件以换行符结尾,它将在REPLY设置为“”的情况下额外运行循环一段时间。在读取时使用
-r | |[-n“$REPLY”];改为执行
。上下文注意:“this”@GordonDavisson的注释中有一个
until
循环,使用一个设置为
read
的布尔控制变量的退出值,当文件的最后一行正确终止时,它确实会将
$REPLY
设置为空字符串。@kopischke:这绝对是一种更简洁的方法。谢谢!