Как я могу получить уникальные значения из массива в bash?
у меня почти такой же вопрос как здесь.
у меня есть массив, который содержит aa ab aa ac aa ad и т. д.
Теперь я хочу выбрать все уникальные элементы из этого массива.
Думал, это будет просто с sort | uniq или sort -u как они упоминали в этом другом вопросе, но ничего не изменилось в массиве...
Код такой:
echo `echo "${ids[@]}" | sort | uniq`
что я делаю не так?
11 ответов:
немного hacky, но это должно сделать это:
echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' 'чтобы сохранить сортированные уникальные результаты обратно в массив, сделайте назначение массива:
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))если ваша оболочка поддерживает herestrings (
bashдолжен), вы можете сэкономитьechoпроцесс путем изменения его к:tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' 'вход:
ids=(aa ab aa ac aa ad)выход:
aa ab ac adобъяснение:
"${ids[@]}"- синтаксис для работы с массивами оболочки, используется ли как частьechoили herestring. Элемент@часть означает "все элементы в массиве"tr ' ' '\n'- преобразование всех пробелов в новые строки. Потому что Ваш массив рассматривается оболочки как элементы в одной строке, разделенные пробелы; и потому, что сортировка ожидает, что ввод будет на отдельных строках.sort -u- сортировка и сохранение только уникальных элементовtr '\n' ' '- преобразуйте новые строки, которые мы добавили ранее, обратно в пробелы.$(...)-Команда Subsitution- в сторону:
tr ' ' '\n' <<< "${ids[@]}"это более эффективный способ сделать:echo "${ids[@]}" | tr ' ' '\n'
если вы используете Bash версии 4 или выше (что должно быть в любой современной версии Linux), вы можете получить уникальные значения массива в bash, создав новый ассоциативный массив, содержащий каждое из значений исходного массива. Что-то вроде этого:
$ a=(aa ac aa ad "ac ad") $ declare -A b $ for i in "${a[@]}"; do b["$i"]=1; done $ printf '%s\n' "${!b[@]}" ac ad ac aa adэто работает, потому что в массиве, каждый ключ может появляться только один раз. Когда
forцикл достигает второго значенияaaнаa[2]перезаписываетb[aa]который был установлен первоначально дляa[0].делать вещи в родной Баш может быть быстрее, чем с помощью труб и внешних инструментов, таких как
sortиuniq.если вы чувствуете себя уверенно, вы можете избежать
forпетли с помощьюprintfвозможность перерабатывать свой формат для нескольких аргументов, хотя это, кажется, требуетeval. (Прекратите читать сейчас, если вас это устраивает.)$ eval b=( $(printf ' ["%s"]=1' "${a[@]}") ) $ declare -p b declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )причина, по которой это решение требует
evalзаключается в том, что значения массива определяются перед словом расщепление. Это означает, что вывод команды подстановки является одно слово а не набор пар ключ=значение.в то время как это использует подобласть, он использует только bash builtins для обработки значений массива. Обязательно оцените свое использование
evalС критическим взглядом. Если вы не уверены на 100%, что chepner или glenn jackman или greycat не найдут ошибок в вашем коде, используйте вместо этого цикл for.
Если ваши элементы массива имеют пробел или любой другой специальный символ оболочки (и вы можете быть уверены, что они этого не делают?) затем, чтобы захватить их в первую очередь (и вы должны просто всегда делать это) выразить свой массив в двойных кавычках! например,
"${a[@]}". Bash буквально интерпретирует это как "каждый элемент массива в отдельном аргумент". В bash это просто всегда работает, всегда.затем, чтобы получить сортированный (и уникальный) массив, мы должны преобразовать его в формат сортировки понимает и сможет преобразовать его обратно в элементы массива bash. Это лучшее, что я придумал:
eval a=($(printf "%q\n" "${a[@]}" | sort -u))к сожалению, это не удается в частном случае пустого массива, превращая пустой массив в массив из 1 пустого элемента (потому что printf имел 0 аргументов, но все еще печатает, как будто у него был один пустой аргумент - см. объяснение). Поэтому вы должны поймать это в if или что-то еще.
объяснение: Формат %q для printf "оболочка экранирует" напечатанное аргумент, именно таким образом, как Баш может восстановиться в чем-то вроде eval! Поскольку каждый элемент является печатной оболочкой, экранированной на его собственной строке, единственным разделителем между элементами является новая строка, и назначение массива принимает каждую строку как элемент, анализируя экранированные значения в литеральный текст.
например
> a=("foo bar" baz) > printf "%q\n" "${a[@]}" 'foo bar' baz > printf "%q\n" ''eval необходимо удалить Экранирование от каждого значения, возвращающегося в массив.
Я понимаю, что на это уже был дан ответ, но он появился довольно высоко в результатах поиска, и это может кому-то помочь.
printf "%s\n" "${IDS[@]}" | sort -uпример:
~> IDS=( "aa" "ab" "aa" "ac" "aa" "ad" ) ~> echo "${IDS[@]}" aa ab aa ac aa ad ~> ~> printf "%s\n" "${IDS[@]}" | sort -u aa ab ac ad ~> UNIQ_IDS=($(printf "%s\n" "${IDS[@]}" | sort -u)) ~> echo "${UNIQ_IDS[@]}" aa ab ac ad ~>
'sort' можно использовать для упорядочения вывода цикла for:
for i in ${ids[@]}; do echo $i; done | sortи устранить дубликаты с "- u":
for i in ${ids[@]}; do echo $i; done | sort -uнаконец, вы можете просто перезаписать массив с уникальными элементами:
ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )
этот также сохранит порядок:
echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[]++'и изменить исходный массив с уникальными значениями:
ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[]++'))
создать новый массив, состоящий из уникальных значений, убедитесь, что Ваш массив не пуст, то выполните одно из следующих действий:
удалить дубликаты записей (с сортировкой)
readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u)удалить дубликаты записей (без сортировки)
readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[]++')Предупреждение: Не пытайтесь сделать что-то вроде
NewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) ). Он разобьется на пробелы.
количество кошек.txt
1 2 3 4 4 3 2 5 6печать строки в столбец:
cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}'1 2 3 4 4 3 2 5 6найти повторяющиеся записи:
cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}' |awk 'x[]++'4 3 2заменить повторяющиеся записи:
cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}' |awk '!x[]++'1 2 3 4 5 6найти только записи Uniq:
cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}1 5 6
Если вы хотите решение, которое использует только внутренние Баш, вы можете установить значения как ключи в ассоциативном массиве, а затем извлечь ключи:
declare -A uniqs list=(foo bar bar "bar none") for f in "${list[@]}"; do uniqs["${f}"]="" done for thing in "${!uniqs[@]}"; do echo "${thing}" doneвыводится
bar foo bar none
без потери исходного заказа:
uniques=($(tr ' ' '\n' <<<"${original[@]}" | awk '!u[]++' | tr '\n' ' '))
попробуйте это, чтобы получить значения uniq для первого столбца в файле
awk -F, '{a[];}END{for (i in a)print i;}'
Comments