我不久前发现了一些用于从文件读取输入的代码,我相信来自 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 $line
is不是空,我们回到非零出口,将我们抛出循环while
。据我所知,(正如 @ilkkachu 指出的,这将捕获输入中缺少尾随换行符的奇怪的最后一行。请注意,这样的文件不是有效的文本文件,因为该行不是有效的行)|| [[ -n "$line" ]]
实际上并没有完成任何事情。
一些东西是偶尔有用就是做while read -r line && [[ -n "$line" ]]
。使用(AND) 意味着如果能够读取一行且该行不为空,&&
则整个语句将仅返回零状态。read
它将导致while
循环在第一个空行处停止。如果我必须猜测的话,这段代码可能已从这样做的一个改编而来 - 作者没有简单地删除测试,而是将其更改&&
为||
.