我有一个名为 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 的值,然后执行其他操作退出循环。我的语法不正确。任何帮助将不胜感激。
简短回答
经过上述评论的讨论后,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
这应该是您的起点:
$ 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
考虑到上述起点,您想做的其他事情同样是微不足道的、高效的、可移植的和健壮的。
您的文件有多大?如果它非常大并且您使用
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
。