在while循环中使用grep会打破循环

时间:2016-04-30 10:15:18

标签: bash loops grep

我想在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或以其他任何方式修复我的脚本?

2 个答案:

答案 0 :(得分:1)

您的代码中存在的问题是此grep

    tmp=$(grep -c $line)

将从 stdin 读取,因此在执行while循环的第一轮中消耗所有行。即首先,您将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

答案 1 :(得分:0)

grep命令读取标准输入的剩余部分。如果你想同时grep使用它,你需要将输入复制到临时文件。

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

uniq -d | tail -n 1

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