删除变量上的重复项而不进行排序

时间:2009-12-09 09:39:00

标签: bash unix shell sorting variables

我有一个包含以下空格分隔条目的变量。

variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"

如何在不排序的情况下删除重复项?

#Something like this.
new_variable="apple lemon papaya avocado grapes mango banana"

我找到了一个脚本,可以完成删除变量的重复,但会对内容进行排序。

#Not something like this.
new_variable=$(echo "$variable"|tr " " "\n"|sort|uniq|tr "\n" " ")
echo $new_variable
apple avocado banana grapes lemon mango papaya

8 个答案:

答案 0 :(得分:20)

new_variable=$( awk 'BEGIN{RS=ORS=" "}!a[$0]++' <<<$variable );

以下是它的工作原理:

RS(输入记录分隔符)设置为空格,以便将$ variable中的每个水果视为记录而不是字段。非排序独特的魔法发生在!a [$ 0] ++。由于awk支持关联数组,因此它使用当前记录($ 0)作为数组a []的键。如果之前没有看到该键,则[$ 0]计算为'0'(awk的未设置索引的默认值),然后否定返回TRUE。然后我利用awk默认为'print $ 0'的事实,如果表达式返回TRUE并且没有给出'{commands}'。最后,[$ 0]然后递增,使得该键不再返回TRUE,因此永远不会打印重复值。 ORS(输出记录分隔符)也设置为空格以模仿输入格式。

产生相同输出的该命令的简洁版本如下:

awk 'BEGIN{RS=ORS=" "}{ if (a[$0] == 0){ a[$0] += 1; print $0}}'

得爱awk =)

修改

如果您需要在纯Bash 2.1+中执行此操作,我建议您:

#!/bin/bash    

variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
temp="$variable"

new_variable="${temp%% *}"

while [[ "$temp" != ${new_variable##* } ]]; do
   temp=${temp//${temp%% *} /}
   new_variable="$new_variable ${temp%% *}"
done

echo $new_variable;

答案 1 :(得分:6)

此管道版本的工作原理是保留原始订单:

variable=$(echo "$variable" | tr ' ' '\n' | nl | sort -u -k2 | sort -n | cut -f2-)

答案 2 :(得分:3)

Pure Bash:

variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"

declare new_value=''

for item in $variable; do
  if [[ ! $new_value =~ $item ]] ; then   # first time?
    new_value="$new_value $item"
  fi
done
new_value=${new_value:1}                  # remove leading blank

答案 3 :(得分:3)

纯粹的便携式sh

words="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
seen=
for word in $words; do
  case $seen in
    $word\ * | *\ $word | *\ $word\ * | $word) 
      # already seen
      ;;
    *)
      seen="$seen $word"
      ;;
  esac
done
echo $seen

答案 4 :(得分:1)

declare -a arr
variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
set -- $variable
count=0
for c in $@
do
    flag=0
    for((i=0;i<=${#arr[@]}-1;i++))
    do
        if [ "${arr[$i]}" == "$c" ] ;then
            flag=1
            break
        fi
    done
    if  [ "$flag" -eq 0 ] ; then
        arr[$count]="$c"
        count=$((count+1))
    fi
done
for((i=0;i<=${#arr[@]}-1;i++))
do
   echo "result: ${arr[$i]}"
done

运行时的结果:

linux# ./myscript.sh
result: apple
result: lemon
result: papaya
result: avocado
result: grapes
result: mango
result: banana

或者如果你想使用gawk

awk 'BEGIN{RS=ORS=" "} (!($0 in a) ){a[$0];print}'

答案 5 :(得分:1)

Z Shell:

% variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
% print ${(zu)variable}                                                               
apple lemon papaya avocado grapes mango banana

答案 6 :(得分:0)

另一个awk解决方案:

#!/bin/bash
variable="apple lemon papaya avocado lemon grapes papaya apple avocado mango banana"
variable=$(printf '%s\n' "$variable" | awk -v RS='[[:space:]]+' '!a[$0]++{printf "%s%s", $0, RT}')
variable="${variable%,*}"
echo "$variable"

输出:

apple lemon papaya avocado grapes mango banana

答案 7 :(得分:0)

Perl解决方案:

perl -le 'for (@ARGV){ $h{$_}++ }; for (keys %h){ print $_ }' $variable

@ARGV是来自$variable的输入参数列表 循环遍历列表,使用循环变量h填充$_哈希 循环遍历h哈希的键,并打印每一个

grapes
avocado
apple
lemon
banana
mango
papaya

此变体打印首先按频率$h{$a} <=> $h{$b}排序的输出 然后按字母顺序$a cmp $b

perl -le 'for (@ARGV){ $h{$_}++ }; for (sort { $h{$a} <=> $h{$b} || $a cmp $b } keys %h){ print "$h{$_}\t$_" }' $variable

1       banana
1       grapes
1       mango
2       apple
2       avocado
2       lemon
2       papaya

此变化产生与最后一个相同的输出 但是,使用输入文件&#39; fruits&#39;而不是输入shell变量,每行一个水果:

perl -lne '$h{$_}++; END{ for (sort { $h{$a} <=> $h{$b} || $a cmp $b } keys %h){ print "$h{$_}\t$_" } }' fruits