58

假设我有以下 Bash 脚本:

while read SCRIPT_SOURCE_LINE; do
  echo "$SCRIPT_SOURCE_LINE"
done

我注意到对于末尾没有换行符的文件,这将有效地跳过最后一行。

我四处寻找解决方案,发现了这个

当读取到达文件结尾而不是行尾时,它确实读入数据并将其分配给变量,但它以非零状态退出。如果您的循环是在“读取时;做事;完成

因此,不要直接测试读取退出状态,而是测试一个标志,并让读取命令从循环体内设置该标志。这样,无论读取退出状态如何,整个循环体都会运行,因为读取只是循环中的命令列表之一,就像任何其他命令一样,而不是循环是否会运行的决定因素。

DONE=false
until $DONE ;do
read || DONE=true
# process $REPLY here
done < /path/to/file.in

如何重写此解决方案以使其行为与while我之前的循环完全相同,即无需对输入文件的位置进行硬编码?

4

8 回答 8

42

我使用以下构造:

while IFS= read -r LINE || [[ -n "$LINE" ]]; do
    echo "$LINE"
done

除了输入中的空字符之外,它几乎适用于任何东西:

  • 以空行开头或结尾的文件
  • 以空格开头或结尾的行
  • 没有终止换行符的文件
于 2011-02-15T23:17:49.663 回答
17

在您的第一个示例中,我假设您正在阅读stdin。要对第二个代码块执行相同的操作,您只需删除重定向并回显 $REPLY:

DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
于 2010-11-12T13:41:40.093 回答
5

使用grepwhile 循环:

while IFS= read -r line; do
  echo "$line"
done < <(grep "" file)

使用grep .而不是grep ""将跳过空行。

笔记:

  1. 使用IFS=使任何行缩进保持不变。

  2. 您几乎应该始终将 -r 选项与 read 一起使用。

  3. 最后没有换行符的文件不是标准的 unix 文本文件。

于 2015-07-14T04:49:06.153 回答
4

尝试使用诸如teecatGNU Coreutils而不是read 。

从标准输入

readvalue=$(tee)
echo $readvalue

从文件

readvalue=$(cat filename)
echo $readvalue
于 2016-11-03T14:23:49.197 回答
2

这是我一直在使用的模式:

while read -r; do
  echo "${REPLY}"
done
[[ ${REPLY} ]] && echo "${REPLY}"

之所以有效,是因为即使循环以非零代码while的出口作为“测试”结束,仍会填充内置变量(或您选择分配的任何变量)。readread$REPLYread

于 2017-03-06T17:24:58.740 回答
1

这里的基本问题是read当它遇到 EOF 时会返回错误级别 1,即使它仍然会正确地提供变量。

因此,您可以在循环中立即使用 errorlevel 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 字符将被忽略
  • 如果你坚持使用来模仿你需要强制检测到 EOFecho的行为(你可以使用条件)catecho -n[ "$eof" == true ]
于 2015-01-03T09:18:48.383 回答
0

@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 启用管道,否则只翻译参数)

于 2016-06-07T09:44:34.653 回答
0

用于读取末尾有或没有换行符的文件:

cat "somefile" | { cat ; echo ; } | while read line; do echo $line; done

来源:我的开源项目https://sourceforge.net/projects/command-output-to-html-table/

于 2021-09-27T00:17:36.753 回答