假设我有以下 Bash 脚本:
while read SCRIPT_SOURCE_LINE; do
echo "$SCRIPT_SOURCE_LINE"
done
我注意到,对于末尾没有换行符的文件,这将有效地跳过最后一行。
我四处寻找解决方案并找到了这个:
当读取到达文件末尾时 行尾,它确实读入 数据并将其分配给变量, 但它以非零状态退出。 如果你的循环是构建的“while 读;做事;完成
所以不要测试读取出口 直接状态,测试标志,并有 读取命令设置该标志 在循环体内。那样 无论读取退出状态如何, 整个循环体运行,因为 read 只是命令列表之一 像其他循环一样,而不是 循环是否会发生的决定因素 完全跑起来。
DONE=false until $DONE ;do read || DONE=true # process $REPLY here done < /path/to/file.in
如何重写此解决方案,使其行为与我之前的
while
循环完全相同,即无需对输入文件的位置进行硬编码?
我使用以下结构:
while IFS= read -r LINE || [ -n "$LINE" ]; do
echo "$LINE"
done
它几乎适用于输入中除空字符之外的任何内容:
在您的第一个示例中,我假设您正在从 stdin 读取。要对第二个代码块执行相同的操作,您只需删除重定向并 echo $REPLY:
DONE=false
until $DONE; do
read -r || DONE=true
printf '%s\n' "$REPLY"
done
将
grep
与 while 循环一起使用:
while IFS= read -r line; do
echo "$line"
done < <(grep "" file)
使用
grep .
而不是 grep ""
将跳过空行。
注:
使用
IFS=
可以保持任何行缩进完好无损。末尾没有换行符的文件不是标准的 unix 文本文件。
尝试使用 GNU Coreutils,而不是 read,例如 tee、cat 等
来自标准输入
readvalue=$(tee)
echo $readvalue
来自文件
readvalue=$(cat filename)
echo $readvalue
这是我一直在使用的模式:
while read -r; do
echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"
这是有效的,因为即使
while
循环结束,因为 read
中的“测试”以非零代码退出,read
仍然填充内置变量 $REPLY
(或您选择分配的任何变量)与 read
)。
这里的基本问题是
read
在遇到 EOF 时将返回错误级别 1,即使它仍然会正确地输入变量。
所以你可以在循环中立即使用
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
请记住:
echo
来模仿 cat
的行为,则需要在检测到 EOF 时强制执行 echo -n
(您可以使用条件 [ "$eof" == true ]
)用于读取末尾有或没有换行符的文件:
cat "somefile" | { cat ; echo ; } | while read line; do echo $line; done
来源:我的开源项目https://sourceforge.net/projects/command-output-to-html-table/
@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 启用管道,否则仅转换参数)