2016-04-30 111 views
1

我想要写在bash脚本,打印标准输入的至少重复线在while循环打破了环

我写了这个代码使用grep:

#!/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) 

似乎t o打破循环,但我找不到原因。 有没有办法绕过在我的代码或任何其他方式来修复我的脚本使用grep?

+0

为什么'circle'是您的预期输出?它不是您示例中的最后一个重复行或最后一行。 – tripleee

+0

它应该是最少的重复,而不是最后的重复;) 不过,下面的答案对我有很大的帮助;) – Konrad

+0

那么你是说第一个独特的行吗?你有多个独特的行;它们都是最不重复的。 – tripleee

回答

0

在你的代码的问题是,这个grep

tmp=$(grep -c $line) 

将从标准输入访问,因此,一轮执行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 
+0

谢谢你的回答:) – Konrad

0

grep命令读取标准输入的其余部分。如果你想同时输入grep和其他的东西,你需要将输入复制到临时文件中。

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

uniq -d | tail -n 1 

更一般地,在一个循环在文件中的每一行运行grep是反模式常常建议移动到awk中或sed相反,如果你可以”用标准工具找到一个简单的管道来实现你的目标。

+0

谢谢,你帮了我很多! – Konrad