Выберите уникальные или уникальные значения из списка в сценарии оболочки UNIX

У меня есть сценарий ksh, который возвращает длинный список значений, разделенных новой строкой, и я хочу видеть только уникальные / отдельные значения. Можно ли это сделать?

Например, скажем, мой вывод - это суффиксы файлов в каталоге:

tar
gz
java
gz
java
tar
class
class

Я хочу увидеть список вроде:

tar
gz
java
class

person brabster    schedule 06.03.2009    source источник


Ответы (8)


Вы можете посмотреть приложения uniq и sort.

./yourscript.ksh | sort | uniq

(К вашему сведению, да, сортировка необходима в этой командной строке, uniq удаляет только повторяющиеся строки, которые идут сразу после друг друга)

РЕДАКТИРОВАТЬ:

Вопреки тому, что было опубликовано Аарон Дигулла в отношении параметров командной строки uniq:

Учитывая следующий ввод:

class
jar
jar
jar
bin
bin
java

uniq выведет все строки ровно один раз:

class
jar
bin
java

uniq -d выведет все строки, которые появляются более одного раза, и напечатает их один раз:

jar
bin

uniq -u выведет все строки, которые появляются ровно один раз, и распечатает их один раз:

class
java
person Matthew Scharley    schedule 06.03.2009
comment
Просто к вашему сведению для опоздавших: ответ @AaronDigulla с тех пор был исправлен. - person mklement0; 18.01.2014
comment
очень хороший момент, эта сортировка необходима в этой командной строке, uniq удаляет только повторяющиеся строки, идущие сразу после друг друга, что я только что узнал !! - person HattrickNZ; 15.04.2015
comment
GNU sort также имеет -u версию для предоставления уникальных значений. - person Mingye Wang; 09.12.2015
comment
Я понял, что uniq швы обрабатывают только соседние строки (по крайней мере, по умолчанию), что означает, что можно sort вводить перед подачей uniq. - person Stphane; 19.02.2016

./script.sh | sort -u

Это то же самое, что и монооксида ответ, но немного более краткий.

person gpojd    schedule 06.03.2009
comment
Вы скромны: ваше решение также будет работать лучше (вероятно, это будет заметно только при больших наборах данных). - person mklement0; 18.01.2014
comment
Думаю, это должно быть эффективнее, чем ... | sort | uniq, потому что выполняется за один выстрел - person Adrian Antunez; 06.08.2018
comment
@AdrianAntunez, возможно, это еще и потому, что sort -u не нужно обновлять отсортированный список каждый раз, когда он находит уже встреченное ранее значение. в то время как sort | должен отсортировать все элементы перед передачей их uniq - person whyer; 10.11.2020

С помощью zsh вы можете сделать это:

% cat infile 
tar
more than one word
gz
java
gz
java
tar
class
class
zsh-5.0.0[t]% print -l "${(fu)$(<infile)}"
tar
more than one word
gz
java
class

Или вы можете использовать AWK:

% awk '!_[$0]++' infile    
tar
more than one word
gz
java
class
person Dimitre Radoulov    schedule 06.03.2009
comment
Умные решения, не требующие сортировки ввода. Предостережения: очень умное, но загадочное awk решение (объяснение см. В stackoverflow.com/a/21200722/45375 ) будет работать с большими файлами, пока количество уникальных строк достаточно мало (поскольку уникальные строки хранятся в памяти). Решение zsh сначала считывает весь файл в память, что может не подходить для больших файлов. Кроме того, как написано, правильно обрабатываются только строки без вложенных пробелов; чтобы исправить это, используйте вместо этого IFS=$'\n' read -d '' -r -A u <file; print -l ${(u)u}. - person mklement0; 18.01.2014
comment
Верный. Или: (IFS=$'\n' u=($(<infile)); print -l "${(u)u[@]}") - person Dimitre Radoulov; 18.01.2014
comment
Спасибо, это проще (при условии, что вам не нужно устанавливать переменные, необходимые вне подоболочки). Мне любопытно, когда вам нужен суффикс [@] для ссылки на все элементы массива - кажется, что - по крайней мере, с версии 5 - он работает без него; или вы просто добавили для наглядности? - person mklement0; 18.01.2014
comment
@ mklement0, ты прав! Я не думал об этом, когда писал пост. Собственно, этого должно хватить: print -l "${(fu)$(<infile)}" - person Dimitre Radoulov; 18.01.2014
comment
Потрясающе, спасибо за обновление вашего сообщения - я взял на себя смелость исправить awk пример вывода. - person mklement0; 18.01.2014

Для больших наборов данных, где сортировка может быть нежелательной, вы также можете использовать следующий сценарий Perl:

./yourscript.ksh | perl -ne 'if (!defined $x{$_}) { print $_; $x{$_} = 1; }'

Это в основном просто запоминает каждую строку вывода, чтобы не выводить ее снова.

Его преимущество перед решением "sort | uniq" в том, что предварительная сортировка не требуется.

person paxdiablo    schedule 06.03.2009
comment
Обратите внимание, что сортировка очень большого файла сама по себе не является проблемой для сортировки; он может сортировать файлы, размер которых превышает доступную RAM + swap. Perl, OTOH, выйдет из строя, если дубликатов будет мало. - person Aaron Digulla; 06.03.2009
comment
Да, это компромисс в зависимости от ожидаемых данных. Perl лучше подходит для огромных наборов данных с большим количеством дубликатов (дисковое хранилище не требуется). Огромный набор данных с небольшим количеством дубликатов должен использовать сортировку (и дисковое хранилище). Небольшие наборы данных могут использовать и то, и другое. Лично я сначала попробую Perl, а если не удастся, переключусь на сортировку. - person paxdiablo; 06.03.2009
comment
Поскольку сортировка дает вам преимущество только в том случае, если она должна быть заменена на диск. - person paxdiablo; 06.03.2009
comment
Это замечательно, когда мне нужно первое появление каждой строки. Сортировка сломает это. - person Bluu; 10.05.2012

Протяните их через sort и uniq. Это удаляет все дубликаты.

uniq -d дает только дубликаты, uniq -u дает только уникальные (удаляет дубликаты).

person Aaron Digulla    schedule 06.03.2009
comment
сначала нужно отсортировать по внешнему виду - person brabster; 06.03.2009
comment
Да, конечно. Или, точнее, вам нужно сгруппировать все повторяющиеся строки вместе. Сортировка делает это по определению;) - person Matthew Scharley; 06.03.2009
comment
Кроме того, uniq -u НЕ является поведением по умолчанию (подробности см. В правке в моем ответе) - person Matthew Scharley; 06.03.2009

С AWK это можно сделать, я нахожу это быстрее, чем сортировка

 ./yourscript.ksh | awk '!a[$0]++'
person Ajak6    schedule 22.05.2017
comment
Это определенно мой любимый способ делать работу, большое спасибо! Возможно, вам не нужны решения sort | uniq, особенно для больших файлов. - person Schmitzi; 30.09.2019

Уникальный, как запрошено (но не отсортированный);
использует меньше системных ресурсов для менее чем ~ 70 элементов (как проверено временем);
написано, чтобы принимать входные данные из stdin,
(или изменять и включать в другой сценарий):
(Bash)

bag2set () {
    # Reduce a_bag to a_set.
    local -i i j n=${#a_bag[@]}
    for ((i=0; i < n; i++)); do
        if [[ -n ${a_bag[i]} ]]; then
            a_set[i]=${a_bag[i]}
            a_bag[i]=$'\0'
            for ((j=i+1; j < n; j++)); do
                [[ ${a_set[i]} == ${a_bag[j]} ]] && a_bag[j]=$'\0'
            done
        fi
    done
}
declare -a a_bag=() a_set=()
stdin="$(</dev/stdin)"
declare -i i=0
for e in $stdin; do
    a_bag[i]=$e
    i=$i+1
done
bag2set
echo "${a_set[@]}"
person FGrose    schedule 28.07.2012

Я получаю лучшие советы, чтобы не дублировать записи в файле

awk '$0 != x ":FOO" && NR>1 {print x} {x=$0} END {print}' file_name | uniq -f1 -u

person Mary Marty    schedule 20.01.2020