提问人:Mathias Bynens 提问时间:11/12/2010 最后编辑:Mathias Bynens 更新时间:10/10/2021 访问量:93106
如果文件末尾没有换行符,如何使用“while read”(Bash)读取文件中的最后一行?
How to use `while read` (Bash) to read the last line in a file if there’s no newline at the end of the file?
问:
假设我有以下 Bash 脚本:
while read SCRIPT_SOURCE_LINE; do
echo "$SCRIPT_SOURCE_LINE"
done
我注意到,对于末尾没有换行符的文件,这将有效地跳过最后一行。
我四处寻找解决方案,发现:
当读取到达文件末尾时 的行尾,它确实在 数据并将其分配给变量, 但它以非零状态退出。 如果你的循环是构造的 “while 读;做事 ;做
因此,与其测试读取出口,不如测试读取出口 状态,测试标志,并具有 读取命令将该标志从 在循环体内。那边 无论读取退出状态如何, 整个循环体运行,因为读取 只是命令列表之一 像其他任何一样在循环中,而不是 循环是否将 跑起来吧。
DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
我怎样才能重写这个解决方案,使其行为与我之前的循环完全相同,即不对输入文件的位置进行硬编码?while
答:
在你的第一个例子中,我假设你正在从 stdin 阅读。要对第二个代码块执行相同的操作,您只需删除重定向和回显$REPLY:
DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
评论
read
[[ ! $REPLY ]] && continue
我使用以下构造:
while IFS= read -r LINE || [[ -n "$LINE" ]]; do
echo "$LINE"
done
它几乎可以处理输入中的空字符以外的任何内容:
- 以空行开头或结尾的文件
- 以空格开头或结尾的行
- 没有终止换行符的文件
评论
/bin/sh
IFS='' read -r LINE || [ -n "$LINE" ]
这里的基本问题是,当它遇到 EOF 时,即使它仍然会正确地馈送变量,也会返回 errorlevel 1。read
因此,您可以立即在循环中使用 errorlevel of,otherwize,不会解析最后的数据。但是你可以这样做:read
eof=
while [ -z "$eof" ]; do
read SCRIPT_SOURCE_LINE || eof=true ## detect eof, but have a last round
echo "$SCRIPT_SOURCE_LINE"
done
如果你想要一个非常可靠的方法来解析你的行,你应该使用:
IFS='' read -r LINE
请记住:
- NUL 字符将被忽略
- 如果您坚持使用来模仿的行为,则需要强制检测到 EOF (您可以使用条件
echo
cat
echo -n
[ "$eof" == true ]
)
使用 while 循环:grep
while IFS= read -r line; do
echo "$line"
done < <(grep "" file)
使用 instead of 将跳过空行。grep .
grep ""
注意:
使用可保持任何行缩进完好无损。
IFS=
末尾没有换行符的文件不是标准的 unix 文本文件。
评论
@Netcoder的答案很好,这种优化消除了虚假的空行,还允许最后一行没有换行符,如果这是原始的。
DONE=false
NL=
until $DONE ;do
if ! read ; then DONE=true ; NL='-n ';fi
echo $NL$REPLY
done
我使用它的变体创建了 2 个函数,以允许包含“[”的文本管道,以保持 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 启用管道,否则只是翻译参数)
与其读取,不如尝试使用 GNU Coreutils,如 tee、cat 等。
与Stdin相比
readvalue=$(tee)
echo $readvalue
从文件
readvalue=$(cat filename)
echo $readvalue
这是我一直在使用的模式:
while read -r; do
echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"
这是有效的,因为即使循环以非零代码从出口的“测试”结束,仍然填充内置变量(或您选择分配的任何变量)。while
read
read
$REPLY
read
对于读取末尾带或不带换行符的文件:
cat "somefile" | { cat ; echo ; } | while read line; do echo $line; done
来源 : 我的开源项目 https://sourceforge.net/projects/command-output-to-html-table/
评论