如果在文件末尾没有换行符,如何使用'read`(Bash)读取文件的最后一行?
比方说,我有以下的Bash脚本:
while read SCRIPT_SOURCE_LINE; do echo "$SCRIPT_SOURCE_LINE" done
我注意到,对于最后没有换行符的文件,这将有效地跳过最后一行。
我search了一个解决scheme, 并发现这一点 :
当读取到达文件结束而不是行结束时,读取数据并将其分配给variables,但是它以非零状态退出。 如果你的循环是“读,做,做,做”
因此,不是直接testing读出口状态,而是testing一个标志,并使读命令在循环体内设置该标志。 这样,无论读取退出状态如何,整个循环体都会运行,因为读取只是循环中的命令列表之一,而不是决定循环是否可以运行的决定性因素。
DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
我怎样才能重写这个解决scheme,使其行为与我之前的while
循环完全相同,即不对input文件的位置进行硬编码?
在你的第一个例子中,我假设你正在读stdin 。 要对第二个代码块执行相同的操作,只需删除redirect并回显$ REPLY:
DONE=false until $DONE ;do read || DONE=true echo $REPLY done
我使用以下构造:
while IFS= read -r LINE || [[ -n "$LINE" ]]; do echo "$LINE" done
它几乎适用于input中的空字符除外:
- 以空行开头或结尾的文件
- 以空白开始或结束的行
- 没有终止换行符的文件
在while循环中使用grep
:
while IFS= read -r line; do echo "$line" done < <(grep "" file)
使用grep .
而不是grep ""
将跳过空行。
注意:
-
使用
IFS=
保持任何行缩进不变。 -
你应该几乎总是使用阅读-r选项。
-
最后没有换行的文件不是标准的unix文本文件。
不要阅读 ,请尝试使用GNU Coreutils,例如T恤 , 猫等。
从标准input
readvalue=$(tee) echo $readvalue
从文件
readvalue=$(cat filename) echo $readvalue
这里的基本问题是,当遇到EOF时, read
将返回错误级别1,即使它仍然会正确地馈送variables。
所以你可以在你的循环中使用readle错误级别,其他的数据不会被parsing。 但是你可以这样做:
eof= while [ -z "$eof" ]; do read SCRIPT_SOURCE_LINE || eof=true ## detect eof, but have a last round echo "$SCRIPT_SOURCE_LINE" done
如果你想要一个非常可靠的方法来parsing你的线,你应该使用:
IFS='' read -r LINE
请记住:
- NUL字符将被忽略
- 如果你坚持用
echo
来模仿cat
的行为,你需要在EOF检测到的时候强制echo -n
(你可以使用条件[ "$eof" == true ]
)
@ Netcoder的答案是好的,这种优化消除虚假的空白行,也允许最后一行不换行,如果这是原来的。
DONE=false NL= until $DONE ;do if ! read ; then DONE=true ; NL='-n ';fi echo $NL$REPLY done
我使用了这个变体来创build2个函数来允许包含'['的文本的pipe道保持grep快乐。 (你可以添加其他翻译)
function grepfix(){ local x="$@"; if [[ "$x" == '-' ]]; then local DONE=false local xx= until $DONE ;do if ! IFS= read ; then DONE=true ; xx="-n "; fi echo ${xx}${REPLY//\[/\\\[} done else echo "${x//\[/\\\[}" fi } function grepunfix(){ local x="$@"; if [[ "$x" == '-' ]]; then local DONE=false local xx= until $DONE ;do if ! IFS= read ; then DONE=true ; xx="-n "; fi echo ${xx}${REPLY//\\\[/\[} done else echo "${x//\\\[/\[}" fi }
(通过 – 如$ 1启用pipe道,否则只是翻译参数)
这是我一直在使用的模式:
while read -r; do echo "${REPLY}" done [[ ${REPLY} ]] && echo "${REPLY}"
这是因为即使while
循环结束,当read
的“testing”以非零代码退出时, read
仍填充内置variables$REPLY
(或者您select分配的任何variables)。