如何在BASH中使用`while read -r line`同时检查另一个文件是否为空?

4

我有一个 while 循环,简化版如下:

while read -r line
do
    (delete some lines from file2.txt)
done < file.txt

如果file2.txt为空,则此while循环不再需要运行。
换句话说,我需要这个:
while read -r line AND file2.txt IS NOT EMPTY
do
    (delete some lines from file2.txt
done < file.txt

我尝试将while read -r line-s file2.txt结合起来,但结果不起作用:
while [ read -r line ] || [ -s file2.txt ]
do
    (delete some lines from file2.txt)
done < file.txt

我该如何使用这个while循环来读取文件中的行,并检查另一个文件是否为空?

1
在原始的 while 循环中,您可以在 (delete some lines from file2.txt) 之后加入一个 if (file2.txt is empty check) ; then break ; fi。我不确定您是如何从 file2.txt 中删除行的,因此不确定您想要进行什么样的“空”检查。 - lurker
1
我使用了 sed -i "/$line/d" ./file2.txt 命令从 file2.txt 文件中删除内容,这个操作是在 while 循环进行时发生的。问题是,file2.txt 很快就变成了空文件,但是 while 循环却继续运行了几分钟,这是不必要的。如果我把 if 条件语句放在 while 条件语句内部,我认为脚本也不会加速。 - Village
1
@Village 这个文件是否被其他程序添加了新行?你是在实现一个队列吗?如果是的话,你可能需要考虑使用 fifo(参见 mkfifo)。我只是不确定 sed -i 是否足够原子化。 - nhed
5个回答

12

将阅读和测试结合起来:

while read -r line && [ -s file2.txt ]
do
  # (delete some lines from file2.txt)
  echo "$line"
done <file.txt

在每次循环迭代之前,这将检查file2.txt是否为空。


1
我会将 [ 测试移到 read 表达式之前,以避免不必要地消耗另一行 stdin。 - kojiro

3
这里可以简化为一个无用的cat使用:
while read -r line
do
    (delete some lines from file2.txt)
done < <(test -s file2.txt && cat file.txt)

$ cat file.txt
foo
bar
baz
$ cat file2.txt
something
$ while read -r line; do echo "$line"; done < <(test -s file2.txt && cat file.txt)
foo
bar
baz
$ > file2.txt
$ while read -r line; do echo "$line"; done < <(test -s file2.txt && cat file.txt)
$

1
如果它真的有用,那么它就不是那么无用了,对吧? :) - Adrian Frühwirth

2
您可以这样做:
while read -r lf1 && [[ -s "path/to/file2" ]] && read -r lf2 <&3; do 
   echo "$lf1"; echo "$lf2"
done <file1 3<file2

这只是一个示例,您可以在while块中添加自己的代码。

测试:

<~/Temp>$ cat file1
line from file1
line2 from file1

<~/Temp>$ cat file2
I am not empty
Yep not empty

<~/Temp>$ while read -r lf1 && [[ -s "/Volumes/Data/jaypalsingh/Temp/file2" ]] && read -r lf2 <&3; do echo "$lf1"; echo "$lf2"; done <file1 3<file2
line from file1
I am not empty
line2 from file1
Yep not empty

<~/Temp>$ >file2

<~/Temp>$ while read -r lf1 && [[ -s "/Volumes/Data/jaypalsingh/Temp/file2" ]] && read -r lf2 <&3; do echo "$lf1"; echo "$lf2"; done <file1 3<file2
<~/Temp>$ 


2
个人而言,我会选择只做。
while read -r line
do
    [ ! -s file2.txt ] && break
    # (delete some lines from file2.txt)
done <file.txt

严格来说,我的解决方案在做任何不同或更好的事情,但这只是个人口味问题。我不喜欢在一个从文件中读取简单行的循环中混合其他条件。我发现这会使代码难以阅读。其他人可能不同意,并且甚至可能建议依靠循环内的break是不好的实践,但我发现这可以让我快速掌握正在发生的事情,而无需减慢速度并在脑海中处理条件,就像你看到外围视野中的停车标志时会停下来一样,而无需直接看标志并阅读字母“STOP”来理解它。像while read -r line这样的东西是如此常见的习语,它们本质上就是常见街道标志的编程等效物。您可以立即识别它们,而无需在脑海中解析它们。无论如何,就像我说的那样,这只是我的个人看法。请随意提出异议。

1

仅对Joe提供的答案进行了一点优化

while [ -s file2.txt ] && read -r line
do
  # (delete some lines from file2.txt)
done <file.txt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接