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


238

У мене є скрипт ksh, який повертає довгий список значень, розділений новий рядок, і я хочу бачити лише унікальні / чіткі значення. Можна це зробити?

Наприклад, скажіть, що моїм результатом є суфікси файлів у каталозі:

tar
gz
java
gz
java
tar
class
class

Я хочу переглянути такий список:

tar
gz
java
class

Відповіді:


432

Можливо, ви захочете переглянути uniqі sortпрограми.

./yourscript.ksh | сортувати | uniq

(FYI, так, сортування необхідне в цьому командному рядку, uniqлише знімає дублікати рядків, які знаходяться відразу один за одним)

Редагувати:

Всупереч тому, що було розміщено Аароном Дігуллою стосовно uniqпараметрів командного рядка:

З огляду на наступний вхід:

клас
баночка
баночка
баночка
бункер
бункер
java

uniq виведе всі рядки рівно один раз:

клас
баночка
бункер
java

uniq -d виведе всі рядки, які з’являються не один раз, і він надрукує їх один раз:

баночка
бункер

uniq -u виведе всі рядки, що з’являються рівно один раз, і він надрукує їх один раз:

клас
java

2
Просто ПІІ для запізнень: Відповідь АаронДігулла з тих пір виправлена.
mklement0

2
Дуже добре, що цей `сортування необхідний у цьому командному рядку, uniq знімає лише дублікати рядків, які знаходяться одразу один за одним`, що я щойно дізнався !!
HattrickNZ

4
У GNU sortє -uверсія і для надання унікальних значень.
Arthur2e5

Я зрозумів, що uniqшви обробляють лише сусідні лінії (принаймні, за замовчуванням), тобто один, який можна sortвводити перед годуванням uniq.
Stphane

85
./script.sh | sort -u

Це те саме , що відповідь на окис , але трохи більш стисло.


6
Ви скромно зауважує: ваше рішення буде також виконувати краще (можливо , тільки помітно з великими наборами даних).
mklement0

Я думаю, що це має бути ефективнішим, ніж ... | sort | uniqтому, що він виконується в одному кадрі
Адріан Антунез

10

Для великих наборів даних, де сортування може бути небажаним, ви також можете скористатись таким сценарієм perl:

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

Це в основному просто запам'ятовує кожен вихідний рядок, щоб він не виводив його знову.

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


2
Зауважте, що сортування дуже великого файлу сама по собі не є проблемою; він може сортувати файли, розміри яких більше, ніж наявна оперативна пам'ять + своп. Perl, OTOH, вийде з ладу, якщо є лише кілька дублікатів.
Аарон Дігулла

1
Так, це компроміс залежно від очікуваних даних. Perl краще для величезного набору даних з багатьма дублікатами (не потрібно зберігати на диску). Величезний набір даних з кількома дублікатами повинен використовувати сортування (та дискове зберігання). Невеликі набори даних можуть використовувати будь-який. Особисто я спершу спробую Perl, переключившись на сортування, якщо це не вдасться.
paxdiablo

Оскільки сортування дає вам лише користь, якщо доведеться перейти на диск.
paxdiablo

5
Це чудово, коли я хочу першого появи кожного рядка. Сортування це порушило б.
Блу

10

З 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

2
Розумні рішення, які не передбачають сортування вхідних даних. Застереження: дуже розумне, але криптичне awkрішення (див. Stackoverflow.com/a/21200722/45375 для пояснення) буде працювати з великими файлами до тих пір, поки кількість унікальних рядків буде невеликою (оскільки унікальні рядки зберігаються в пам'яті ). zshРішення читає весь файл в пам'ять першим, яка не може бути варіантом з великими файлами. Крім того, як написано, лише рядки без вбудованих пробілів обробляються правильно; щоб виправити це, скористайтеся IFS=$'\n' read -d '' -r -A u <file; print -l ${(u)u}натомість.
mklement0

Правильно. Або:(IFS=$'\n' u=($(<infile)); print -l "${(u)u[@]}")
Димитрій Радулов

1
Дякую, це простіше (якщо припустити, що вам не потрібно встановлювати змінні, необхідні за межами нижньої частини). Мені цікаво, коли вам потрібен [@]суфікс для посилання на всі елементи масиву - здається, що - принаймні, у версії 5 - він працює без нього; чи ви просто додали його для наочності?
mklement0

1
@ mklement0, ти маєш рацію! Я не думав про це, коли писав пост. Насправді цього має бути достатньо:print -l "${(fu)$(<infile)}"
Димитре Радулов

1
Фантастично, дякую за оновлення вашої публікації - я також зумів виправити awkвихідний зразок.
mklement0

9

Труби їх через sortі uniq. Це видаляє всі дублікати.

uniq -dдає лише дублікати, uniq -uдає лише унікальні (смужки дублікатів).


Ви повинні розібратися спочатку за зовнішнім виглядом
брабстер

1
Так ти зробиш. Або, якщо точніше, потрібно згрупувати всі повторювані рядки разом. Хоча сортування робить це за визначенням;)
Метью Шарлі

Крім того, uniq -uНЕ поведінка за замовчуванням (див. Редагування у моїй відповіді для деталей)
Меттью Шарлі

7

З AWK ви можете це зробити, я вважаю це швидше, ніж сортування

 ./yourscript.ksh | awk '!a[$0]++'

Це, безумовно, мій улюблений спосіб зробити роботу, велике спасибі! Особливо для великих файлів, сортування | uniq-рішення, мабуть, не те, що потрібно.
Шмітці

1

Унікальний, за запитом, (але не відсортований);
використовує менше системних ресурсів для менше ~ 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[@]}"

0

Я отримую кращі поради щодо отримання не повторюваних записів у файлі

awk '$0 != x ":FOO" && NR>1 {print x} {x=$0} END {print}' file_name | uniq -f1 -u
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.