shell脚本中的While循环条件

问题描述 投票:0回答:3

我有一个名为 file.txt 的文本文件,其中包含以下条目:-

healthy
healthy
healthy
healthy
healthy
unhealthy
initial
healthy
initial
healthy

现在我使用以下命令计算此文件中健康、初始和不健康的数量:-

grep -c healthy file.txt
grep -c unhealthy file.txt
grep -c initial file.txt

现在我想要 shell 脚本中的循环条件来为我执行此操作:-

while [ $(grep -c "healthy" file.txt) -lt 6 -a $(grep -c "unhealthy" file.txt) != 0 -a $(grep -c "initial" file.txt) != 0 ]
do
bla bla bla
done

基本上我想做的是,对于这个动态文件,其条目将作为其他脚本的一部分不断变化,我希望只要文件中的健康计数小于等于 6 并且也计数,就会发生循环不健康的值是大于 0 的值,并且初始计数是大于 0 的值,然后执行其他操作退出循环。我的语法不正确。任何帮助将不胜感激。

bash shell while-loop
3个回答
2
投票

简短回答

经过上述评论的讨论后,OP 和我确定所提议的循环中唯一真正的问题是

grep -c healthy
也会匹配
unhealthy
,但除此之外,循环已经按预期工作。

\b
应用于指示单词边界,如
grep -c '\bhealthy'
所示,形成循环:

while [ $(grep -c '\bhealthy' file.txt) -lt 6 -a $(grep -c "unhealthy" file.txt) != 0 -a $(grep -c "initial" file.txt) != 0 ]
do
   bla bla bla
done

编辑:正如@IanW在评论中指出的那样,您还可以使用

grep -c -w word
代替添加
\b
,这就像在每个单词之前和之后添加
\b
一样。

使其面向未来

还值得重复 @CharlesDuffy 上面的建议,以避免

-a
-o
,因为它们被标记为过时,而更喜欢
[ ... ] && [ ... ]
。对于长期稳定的代码来说,这是一个不错的选择。

所以现在循环看起来像这样:

while [ $(grep -c '\bhealthy' file.txt) -lt 6 ] && [ $(grep -c "unhealthy" file.txt) != 0 ] && [ $(grep -c "initial" file.txt) != 0 ]
do
   bla bla bla
done

或者使其特定于 bash

最后我想指出,如果这是专门在

bash
而不是
sh
中执行,那么
[[ ... ]]
会更快,因为它是由 bash 本身解释的,而不是调用程序
test
,后者
[ 
是 的别名。
[[ ... ]]
是我个人的偏好,但与 POSIX 标准命令不同,它将来可能会崩溃并且不兼容所有 shell。但它支持我认为更好的语法,并且通常更易于使用,特别是不需要一直引用变量。有关该主题的有趣讨论,请参阅 bash 中的双方括号与单方括号

所以我自己的首选格式是:

while [[ $(grep -c '\bhealthy' file.txt) -lt 6 && $(grep -c "unhealthy" file.txt) != 0 && $(grep -c "initial" file.txt) != 0 ]]
do
   bla bla bla
done

1
投票

这应该是您的起点:

$ awk '{c[$1]++} END{for (i in c) print i, c[i]}' file
healthy 7
initial 2
unhealthy 1

写下你想要采取行动的条件,你可以直接写下:

$ awk '
    { c[$1]++ }
    END { exit ( (c["healthy"] <= 6) && (c["unhealthy"] > 0) && (c["initial"] > 0) ? 1 : 0 ) }
' file
$ echo $?
0

$ awk '
    { c[$1]++ }
    END { exit ( (c["healthy"] <= 8) && (c["unhealthy"] > 0) && (c["initial"] > 0) ? 1 : 0 ) }
' file
$ echo $?
1

并将它们用作:

while awk '...' file; do
    your stuff
done

考虑到上述起点,您想做的其他事情同样是微不足道的、高效的、可移植的和健壮的。


0
投票

您的文件有多大?如果它非常大并且您使用

grep
扫描它三次,可能会使您的脚本不必要地变慢。

您可以使用 AWK 一次遍历文件来计算匹配项:

read -r u_count h_count i_count <<< <(awk '{arr[$1]++} END {print arr["unhealthy"] arr["healthy"] arr["initial"]}'
while (( u_count < 6 && h_count != 0 && i_count != 0 ))

只要数据文件看起来像您发布的示例,或者即使这些单词后面有其他空格分隔的字段,这就可以工作。如果这些单词不是每行的第一个单词,则可以适当修改 AWK 脚本。

除非这些计数在循环内发生变化,否则您可能只想使用

if
而不是
while

© www.soinside.com 2019 - 2024. All rights reserved.