`while read -r line || 是什么意思? [[ -n $line ]]` 是什么意思?

`while read -r line || 是什么意思? [[ -n $line ]]` 是什么意思?

我不久前发现了一些用于从文件读取输入的代码,我相信来自 Stack Exchange,我能够适应我的需求:

while read -r line || [[ -n "$line" ]]; do
    if [[ $line != "" ]]
    then
        ((x++))
        echo "$x: $line"
    # <then do something with $line>
    fi
done < "$1"

我现在正在审查我的脚本并试图了解它在做什么...我不明白这个语句在做什么:

while read -r line || [[ -n "$line" ]];

我知道该-r选项表示我们正在将原始文本读入,但我对该语句的部分$line感到困惑。|| [[ -n "$line" ]]有人可以解释一下这是做什么的吗?

答案1

[[ -n "$line" ]]测试$line(刚刚读取的变量read)是否不为空。它很有用,因为read如果并且的话返回成功除非它看到一个换行符在文件结束之前。如果输入包含最后没有换行符的行片段,则此测试将捕获该行,并且循环也将处理最后的不完整行。如果没有额外的测试,这样一个不完整的行将被读入$line,但被循环忽略。

我说“不完整的行”,因为 POSIX 定义一个文本文件一条线需要在每行末尾有一个换行符。其他工具read也可以关心,例如wc -l 计算换行符的数量,因此忽略最后一条不完整的行。参见例如在文件末尾添加新行有什么意义?为什么文本文件应该以换行符结尾?就这样。

当然,该cmd1 || cmd2结构与 C 中的等效结构类似。如果第一个命令返回错误状态,则运行第二个命令,结果是最后执行的命令的退出状态。

比较:

$ printf 'foo\nbar' | ( while read line; do
                            echo "in loop: $line"
                        done
                        echo "finally: $line"
                      )
in loop: foo
finally: bar

$ printf 'foo\nbar' | ( while read line || [[ -n $line ]]; do 
                            echo "in loop: $line"
                        done
                        echo "finally: $line"
                      )
in loop: foo
in loop: bar
finally: 

答案2

这有点令人困惑为什么它会在那里,但可以直接解释它的作用:||是一个 OR 语句,[[ -n只要"$line"长度不为零,就返回 true(成功)。这是令人困惑的地方:当存在成功(0)退出状态时,while 循环继续运行。read继续读取行并返回 0 退出状态,直到到达文件末尾——即使这些行是空白的。[[ -n "$line" ]]仅当返回非零退出代码时才会执行read,此时$line将为空。由于测试返回 true if $lineis不是空,我们回到非零出口,将我们抛出循环while据我所知,|| [[ -n "$line" ]]实际上并没有完成任何事情。(正如 @ilkkachu 指出的,这将捕获输入中缺少尾随换行符的奇怪的最后一行。请注意,这样的文件不是有效的文本文件,因为该行不是有效的行

一些东西偶尔有用就是做while read -r line && [[ -n "$line" ]]。使用(AND) 意味着如果能够读取一行且该行不为空,&&则整个语句将仅返回零状态。read它将导致while循环在第一个空行处停止。如果我必须猜测的话,这段代码可能已从这样做的一个改编而来 - 作者没有简单地删除测试,而是将其更改&&||.

相关内容