Используя ваш пример для создания входа для тестирования:
printf 'one\nfour\ntwo\nthree\nfour\n'
Самый простой способ справиться с этим просто отменить данные, дважды. Следующие работы в BSD и OS X:
command | tail -r | awk '!a[$0]++' | tail -r
Но вариант -r
не является универсальным. Если вы на Linux, вы можете создать такой же эффект с tac
команды (напротив cat
), который является частью Coreutils:
command | tac | awk '!a[$0]++' | tac
Если ни одна из этих работ (то есть вы на HP/UX или старше Solaris и т.д.), вы можете быть в состоянии полностью изменить вещи, используя sed
:
command | sed '1!G;h;$!d' | awk '!a[$0]++' | sed '1!G;h;$!d'
конечно, вы могли бы сделать это с Perl, а также:
command | perl -e 'print reverse <>' | awk '!a[$0]++' | perl -e 'print reverse <>'
Но если Perl доступен на вашей системе, вы можете также упростить трубу и пропустить AWK полностью:
command | perl -e '$a{$_}++ or print for reverse <>'
Я никогда не любил Perl, хотя, и я делать как делать вещи в скорлупе. Если вы находитесь в Баше (версии 4 или выше), и вы не заботитесь о производительности, вы можете реализовать массив прямо в оболочке:
mapfile -t a < <(command)
declare -A b;
for ((i=${#a[@]}-1 ; i>=0; i--)); do ((b[${a[$i]}]++)) || echo "${a[$i]}"; done
нет внешних инструментов, необходимых. :-)
UPDATE:
Inspired (или, возможно, вызов) от sudo_O's answer, вот еще один вариант, который работает в чистом AWK на BSD (т.е. не требует GNU AWK):
command | awk '{a[NR]=$0;b[$0]=NR} END {for(i=1;i<=NR;i++) if(i==b[a[i]]) print a[i]}'
Обратите внимание, что это сохраняет все входные данные в памяти дважды, поэтому может быть неуместным для больших наборов данных.
NB: Perl был изобретен, чтобы улучшить, используя AWK + СЭД + shell + ... – Dogweather