【问题标题】:Using grep in while loop breaks the loop在 while 循环中使用 grep 会中断循环
【发布时间】:2016-04-30 10:15:18
【问题描述】:

我想在 bash 中编写一个脚本,打印出标准输入中重复最少的行

我写了这段代码:

#!/bin/bash
var=1000
while read line
do
    tmp=$(grep -c $line)
    if [ $tmp -lt $var ]
    then
        var=$tmp
        out=$line
    fi
done
var="$var $out"
echo $var

但是例如当使用这样的测试时

id1
id2
id3
id1
square
id1
id2
id3
id1
circle
id2
id2

程序只进入循环一次,因此输出错误

3 id1

什么时候应该是正确的

1 square

这一行

tmp=$(grep -c $line)

似乎打破了循环,但我不知道为什么。 有什么方法可以绕过在我的代码中使用 grep 或任何其他方法来修复我的脚本?

【问题讨论】:

  • 为什么circle 是您的预期输出?在您的示例中,它既不是最后一个重复行,也不是最后一个唯一行。
  • 它应该是最少重复的,而不是最后一次重复;)不过,您在下面的回答对我帮助很大;)
  • 那么你的意思是第一个唯一的行吗?您有多个独特的线路;它们都是最少重复的。
  • 不,我想我的英语技能没有让我说得足够清楚,如果标准输入中有唯一的一行,它也应该打印出来,假设我们有一行包含单词: square ,两行包含 word: circle 和三行包含 word: triangle 。它应该打印“正方形”,因为它只在文件中出现一次(出现次数最少)
  • 这很清楚,但如果每个都有三个,你只想要第一个吗?

标签: bash loops grep


【解决方案1】:

你的代码中的问题是这个grep

    tmp=$(grep -c $line)

将从 stdin 读取,因此在执行 while 循环的第一轮消耗所有行。 IE。首先你将read 的第一行变成$line。然后你会在 stdin 的其余部分为这个字符串 grep

您可以通过使用临时文件来修复您的代码,例如:

#!/bin/bash
tmpfile=$(mktemp)
cat > "$tmpfile"
min=0
while IFS= read -r line; do
    count=$(grep -c "$line" $tmpfile)
    if (( min == 0 || (count < min) )); then
        min=$count
        out="$min $line"
    fi
done < <(sort -u "$tmpfile")
rm "$tmpfile"
echo "$out"

但这当然是非常糟糕的解决方案,因为它使用临时文件并多次打开输入文件。最好使用类似的东西:

#!/bin/bash
sort | uniq -c | sort -n | head -1

【讨论】:

  • 谢谢你的回答:)
【解决方案2】:

grep 命令读取标准输入的剩余部分。如果您想同时使用 grep 并对其执行其他操作,则需要将输入复制到临时文件中。

解决您的问题的一个更简单的方法是

uniq -d | tail -n 1

更一般地说,在文件循环中的每一行上运行 grep 是反模式,如果您找不到使用标准工具的简单管道来实现您的目标,这通常建议您改用 Awk 或 sed .

【讨论】:

  • 谢谢,你帮了我很多忙!
猜你喜欢
  • 2021-01-30
  • 2013-11-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-03
  • 2014-03-22
  • 2019-02-01
  • 2017-11-14
  • 2019-05-05
相关资源
最近更新 更多