如果文件末尾没有换行符,如何使用“while read”(Bash)读取文件中的最后一行?
假设我有以下 Bash 脚本:
while read SCRIPT_SOURCE_LINE; do
echo "$SCRIPT_SOURCE_LINE"
done
我注意到对于末尾没有换行符的文件,这将有效地跳过最后一行。
我四处寻找解决方案并找到了这个 :
当读取到达文件末尾时 行尾,它确实读入 数据并将其分配给变量, 但它以非零状态退出。 如果你的循环是构建的“while 读;做事;完成
所以不要测试读取出口 直接状态,测试标志,并有 读取命令设置该标志 在循环体内。那样 无论读取退出状态如何, 整个循环体运行,因为 read 只是命令列表之一 像其他循环一样,而不是 循环是否会发生的决定因素 完全跑起来。
<前><代码>完成=假 直到 $DONE ;do 阅读||完成=真 # 在这里处理 $REPLY 完成< /路径/到/文件.in
如何重写此解决方案,使其行为与我之前使用的 while 循环完全相同,即无需对输入文件的位置进行硬编码?
Let’s say I have the following Bash script:
while read SCRIPT_SOURCE_LINE; do
echo "$SCRIPT_SOURCE_LINE"
done
I noticed that for files without a newline at the end, this will effectively skip the last line.
I’ve searched around for a solution and found this:
When read reaches end-of-file instead
of end-of-line, it does read in the
data and assign it to the variables,
but it exits with a non-zero status.
If your loop is constructed "while
read ;do stuff ;doneSo instead of testing the read exit
status directly, test a flag, and have
the read command set that flag from
within the loop body. That way
regardless of reads exit status, the
entire loop body runs, because read
was just one of the list of commands
in the loop like any other, not a
deciding factor of if the loop will
get run at all.DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
How can I rewrite this solution to make it behave exactly the same as the while
loop I was having earlier, i.e. without hardcoding the location of the input file?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我使用以下构造:
它适用于输入中除空字符之外的几乎所有内容:
I use the following construct:
It works with pretty much anything except null characters in the input:
在您的第一个示例中,我假设您正在从 stdin 读取内容。要对第二个代码块执行相同的操作,您只需删除重定向并 echo $REPLY:
In your first example, I'm assuming you are reading from stdin. To do the same with the second code block, you just have to remove the redirection and echo $REPLY:
将
grep
与 while 循环一起使用:使用
grep .
而不是grep ""
将跳过空行。注意:
使用
IFS=
可以保持任何行缩进不变。您几乎应该始终使用 -r 选项进行读取。
末尾没有换行符的文件是不是标准的 unix 文本文件。
Using
grep
with while loop:Using
grep .
instead ofgrep ""
will skip the empty lines.Note:
Using
IFS=
keeps any line indentation intact.You should almost always use the -r option with read.
File without a newline at the end isn't a standard unix text file.
使用 GNU Coreutils,例如 tee、cat 等,而不是 read
stdin 中
尝试从文件的
Instead of read, try to use GNU Coreutils like tee, cat, etc.
from stdin
from file
这是我一直在使用的模式:
它之所以有效,是因为即使
while
循环随着read
中的“测试”以非零代码退出而结束,read
仍然填充内置变量$REPLY
(或您选择使用read
分配的任何变量)。This is the pattern I've been using:
Which works because even tho' the
while
loop ends as the "test" from theread
exits with a non-zero code,read
still populates the inbuilt variable$REPLY
(or whatever variables you choose to assign withread
).这里的基本问题是,
read
在遇到 EOF 时将返回错误级别 1,即使它仍然正确地输入变量。所以你可以在循环中立即使用
read
的errorlevel,否则,最后的数据将不会被解析。但你可以这样做:如果你想要一个非常可靠的方法来解析你的行,你应该使用:
记住:
echo
来模仿的行为 >cat
您需要在检测到 EOF 时强制执行echo -n
(您可以使用条件[ "$eof" == true ]
)The basic issue here is that
read
will return errorlevel 1 when it encounters EOF, even if it'll still correctly feed the variable.So you can use errorlevel of
read
right away in your loop, otherwize, the last data won't be parsed. But you could do this:If you want a very solid way to parse your lines, you should use:
Remember that:
echo
to mimick the behavior ofcat
you'll need to force anecho -n
upon EOF detected (you can use the condition[ "$eof" == true ]
)用于读取末尾有或没有换行符的文件:
来源:我的开源项目 https://sourceforge.net/projects/command-output-to-html-table/
for reading files with or without a newline at the end:
Source : My open source project https://sourceforge.net/projects/command-output-to-html-table/
@Netcoder 的答案很好,这种优化消除了虚假的空行,还允许最后一行没有换行符(如果原始行是这样的话)。
我使用它的一个变体创建了 2 个函数,以允许包含“[”的文本管道传输,以使 grep 满意。 (您可以添加其他翻译)
(传递 - 作为 $1 启用管道,否则仅翻译参数)
@Netcoder's answer is good, this optimisation eliminates spurious blank lines, also allows for the last line not to have a newline, if that's how the original was.
I used a variant of this to create 2 functions to allow piping of text that includes a '[' to keep grep happy. (you can add other translations)
(passing - as $1 enables pipe otherwise just translates arguments)