首页 文章

如果文件末尾没有换行符,如何使用`read read`(Bash)读取文件中的最后一行?

提问于
浏览
43

假设我有以下Bash脚本:

while read SCRIPT_SOURCE_LINE; do
  echo "$SCRIPT_SOURCE_LINE"
done

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

我一直在寻找解决方案and found this

当读取到达文件结尾而不是行尾时,它会读入数据并将其分配给变量,但它会以非零状态退出 . 如果您的循环是“在读取时构建的;执行操作;完成所以不要直接测试读取退出状态,测试一个标志,并让读取命令在循环体内设置该标志 . 这样无论读取退出状态如何,整个循环体运行,因为read只是循环中的命令列表之一,而不是任何其他循环中的命令列表,而不是循环是否会运行的决定因素.DONE = false
直到$ DONE;做
读|| DONE = TRUE
#process $ REPLY here
完成</path/to/file.in

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

7 回答

  • 0

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

    DONE=false
    until $DONE ;do
    read || DONE=true
    echo $REPLY
    done
    
  • 31

    我使用以下构造:

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

    除了输入中的空字符外,它几乎可以使用任何东西:

    • 以空行开头或结尾的文件

    • 以空格开头或结尾的行

    • 没有终止换行符的文件

  • 4

    使用带有while循环的 grep

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

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

    注意:

  • 1

    而不是阅读,尝试使用像T恤,猫等GNU Coreutils .

    来自stdin

    readvalue=$(tee)
    echo $readvalue
    

    从文件

    readvalue=$(cat filename)
    echo $readvalue
    
  • 13

    这里的基本问题是 read 会在遇到EOF时返回errorlevel 1,即使它仍然正确地提供变量 .

    所以你可以在你的循环中立即使用 read 的errorlevel,另外,不会解析最后的数据 . 但你可以这样做:

    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字符将被忽略

    • 如果你坚持使用 echo 来模仿 cat 的行为你需要在检测到EOF时强制 echo -n (你可以使用条件 [ "$eof" == true ]

  • 2

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

  • 0

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

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

    这是有效的,因为即使 while 循环从 read 出口"test"以非零代码结束, read 仍然填充内置变量 $REPLY (或您选择用 read 分配的任何变量) .

相关问题