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

K. *_*ert 8 read

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

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

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

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

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

ilk*_*chu 7

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

我说的是“不完整的行”,因为文本文件的 POSIX 定义要求在每行末尾有一个换行符。其他工具read也可以关心,例如wc -l 计算换行符,因此忽略最后的不完整行。参见例如在文件末尾添加新行有什么意义?为什么要文本文件用新行结束?在 SO 上。

cmd1 || cmd2构造当然就像 C 中的等价物一样。如果第一个命令返回假状态,则第二个命令运行,结果是执行的最后一个命令的退出状态。

相比:

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

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