首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >shell脚本中的While循环条件

shell脚本中的While循环条件
EN

Stack Overflow用户
提问于 2019-08-02 03:56:00
回答 3查看 180关注 0票数 2

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

代码语言:javascript
运行
复制
healthy
healthy
healthy
healthy
healthy
unhealthy
initial
healthy
initial
healthy

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

代码语言:javascript
运行
复制
grep -c healthy file.txt
grep -c unhealthy file.txt
grep -c initial file.txt

现在我需要shell脚本中的一个循环条件,它可以为我完成以下操作:-

代码语言:javascript
运行
复制
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的值,那么就会发生循环,然后执行其他退出循环的操作。我没有得到正确的语法。这里的任何帮助都将不胜感激。

EN

回答 3

Stack Overflow用户

发布于 2019-08-02 10:57:34

The short answer

在上面的评论中进行了讨论后,OP和我确定了所提出的循环中唯一的真正问题是grep -c healthy也将匹配unhealthy,但除此之外,该循环已经按预期工作。

就像在grep -c '\bhealthy'中一样,\b应该用来表示单词边界,从而产生循环:

代码语言:javascript
运行
复制
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,因为它们被标记为过时,而更喜欢[ ... ] && [ ... ]。对于长期稳定的代码来说,这是一个很好的选择。

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

代码语言:javascript
运行
复制
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都兼容。但它支持一种我认为更好的语法,而且通常更易于使用,特别是不需要总是引用变量。有关该主题的有趣讨论,请参阅double vs single square brackets in bash

所以我自己更喜欢的格式是:

代码语言:javascript
运行
复制
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
票数 2
EN

Stack Overflow用户

发布于 2019-08-02 11:06:58

你搞错了。这应该是你的起点:

代码语言:javascript
运行
复制
$ awk '{c[$1]++} END{for (i in c) print i, c[i]}' file
healthy 7
initial 2
unhealthy 1

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

代码语言:javascript
运行
复制
$ 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

并将它们用作:

代码语言:javascript
运行
复制
while awk '...' file; do
    your stuff
done

无论您想要做什么,都同样是琐碎的、高效的、可移植的和健壮的。

票数 1
EN

Stack Overflow用户

发布于 2019-08-02 06:46:41

你的文件有多大?如果它非常大,并且您使用grep扫描它三次,这可能会使您的脚本变得不必要地慢。

您可以使用AWK对文件中的一次匹配进行计数:

代码语言:javascript
运行
复制
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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57316169

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档