Um pouco maluco, mas deve bastar:
echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '
Para salvar os resultados exclusivos classificados de volta em uma matriz, faça a atribuição de Array :
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
Se seu shell oferece suporte a cadeias de caracteres ( bash
deveria), você pode poupar um echo
processo alterando-o para:
tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '
Entrada:
ids=(aa ab aa ac aa ad)
Resultado:
aa ab ac ad
Explicação:
"${ids[@]}"
- Sintaxe para trabalhar com matrizes de shell, sejam usadas como parte de echo
ou uma cadeia de caracteres. A @
parte significa "todos os elementos da matriz"
tr ' ' '\n'
- Converta todos os espaços em novas linhas. Porque seu array é visto pelo shell como elementos em uma única linha, separados por espaços; e porque o sort espera que a entrada esteja em linhas separadas.
sort -u
- classificar e reter apenas elementos únicos
tr '\n' ' '
- converte as novas linhas que adicionamos anteriormente em espaços.
$(...)
- Substituição de Comando
- À parte:
tr ' ' '\n' <<< "${ids[@]}"
é uma maneira mais eficiente de fazer:echo "${ids[@]}" | tr ' ' '\n'
uniq=($(printf "%s\n" "${ids[@]}" | sort -u)); echo "${uniq[@]}"
printf
dessa forma (dar mais argumentos do que formato de strings)sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
. Sem os parênteses adicionais, estava sendo fornecido como uma string.... | uniq | ...
vez de... | sort -u | ...
.uniq
remove apenas duplicatas consecutivas . No exemplo desta resposta,sorted_unique_ids
ficará idêntico ao originalids
. Para preservar a ordem, tente... | awk '!seen[$0]++'
. Consulte também stackoverflow.com/questions/1444406/… .Se você estiver executando o Bash versão 4 ou superior (o que deve ser o caso em qualquer versão moderna do Linux), poderá obter valores de array exclusivos em bash criando um novo array associativo que contém cada um dos valores do array original. Algo assim:
$ 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
Isso funciona porque em qualquer array (associativo ou tradicional, em qualquer idioma), cada chave só pode aparecer uma vez. Quando o
for
loop chega ao segundo valor deaa
ina[2]
, ele substitui ob[aa]
que foi definido originalmente paraa[0]
.Fazer coisas no bash nativo pode ser mais rápido do que usar canais e ferramentas externas como
sort
euniq
, embora para conjuntos de dados maiores você provavelmente verá um desempenho melhor se usar uma linguagem mais poderosa como awk, python, etc.Se você estiver se sentindo confiante, pode evitar o
for
loop usandoprintf
a capacidade de reciclar seu formato para vários argumentos, embora isso pareça exigireval
. (Pare de ler agora se você estiver bem com isso.)$ eval b=( $(printf ' ["%s"]=1' "${a[@]}") ) $ declare -p b declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )
O motivo pelo qual essa solução exige
eval
é que os valores da matriz sejam determinados antes da divisão das palavras. Isso significa que a saída da substituição do comando é considerada uma única palavra em vez de um conjunto de pares chave = valor.Embora use um subshell, ele usa apenas bash builtins para processar os valores do array. Certifique-se de avaliar o uso do
eval
com um olhar crítico. Se você não estiver 100% confiante de que chepner ou glenn jackman ou greycat não encontrariam nenhuma falha em seu código, use o loop for.fonte
Sei que isso já foi respondido, mas apareceu bem alto nos resultados da pesquisa e pode ajudar alguém.
printf "%s\n" "${IDS[@]}" | sort -u
Exemplo:
~> 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 ~>
fonte
ids=(ab "a a" ac aa ad ac aa);IFS=$'\n' ids2=(`printf "%s\n" "${ids[@]}" |sort -u`)
então adicionei oIFS=$'\n'
sugerido por @gniourf_gniourfIFS=$'\n'; ids2=(...)
vez que a atribuição temporária antes das atribuições de variáveis não é possível. Em vez disso usar esta construção:IFS=$'\n' read -r -a ids2 <<<"$(printf "%s\n" "${ids[@]}" | sort -u)"
.Se seus elementos de array têm espaço em branco ou qualquer outro caractere especial de shell (e você pode ter certeza que eles não têm?) Então, para capturá-los antes de tudo (e você deve sempre fazer isso), expresse seu array em aspas duplas! por exemplo
"${a[@]}"
. O Bash interpretará isso literalmente como "cada elemento do array em um argumento separado ". No bash, isso simplesmente sempre funciona, sempre.Então, para obter um array ordenado (e único), temos que convertê-lo em um formato que a classificação entenda e ser capaz de convertê-lo de volta em elementos do array bash. Este é o melhor que eu fiz:
eval a=($(printf "%q\n" "${a[@]}" | sort -u))
Infelizmente, isso falha no caso especial do array vazio, transformando o array vazio em um array de 1 elemento vazio (porque printf tinha 0 argumentos, mas ainda imprime como se tivesse um argumento vazio - veja a explicação). Então você tem que pegar isso em um se ou algo.
Explicação: O formato% q para printf "shell escapa" do argumento impresso, da mesma forma que o bash pode se recuperar em algo como eval! Como cada elemento é impresso com escape de shell em sua própria linha, o único separador entre os elementos é a nova linha, e a atribuição da matriz leva cada linha como um elemento, analisando os valores de escape em texto literal.
por exemplo
> a=("foo bar" baz) > printf "%q\n" "${a[@]}" 'foo bar' baz > printf "%q\n" ''
O eval é necessário para retirar o escape de cada valor que volta ao array.
fonte
uniq
vez desort -u
.uniq
não funciona corretamente em listas não classificadas, portanto, sempre deve ser usado em combinação comsort
.'sort' pode ser usado para ordenar a saída de um loop for:
for i in ${ids[@]}; do echo $i; done | sort
e elimine duplicatas com "-u":
for i in ${ids[@]}; do echo $i; done | sort -u
Por fim, você pode apenas substituir sua matriz com os elementos exclusivos:
ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )
fonte
ids=( `for i in ${ids[@]}; do echo $i; done | uniq` )
uniq
apenas remove as linhas duplicadas adjacentes .este também preservará a ordem:
echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'
e para modificar a matriz original com os valores únicos:
ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++'))
fonte
uniq
. Ele precisa de classificação, onde awk não, e a intenção dessa resposta é preservar a ordem quando a entrada não é classificada.Para criar uma nova matriz consistindo em valores únicos, certifique-se de que sua matriz não esteja vazia e execute um dos seguintes procedimentos:
Remover entradas duplicadas (com classificação)
readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u)
Remova entradas duplicadas (sem classificação)
readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[$0]++')
Aviso: Não tente fazer algo assim
NewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) )
. Ele vai quebrar em espaços.fonte
sort -u
para seruniq
.uniq
mescla apenas linhas duplicadas adjacentes, portanto, não é o mesmo queawk '!x[$0]++'
.fonte
Sem perder o pedido original:
uniques=($(tr ' ' '\n' <<<"${original[@]}" | awk '!u[$0]++' | tr '\n' ' '))
fonte
Se você deseja uma solução que usa apenas componentes internos do bash, pode definir os valores como chaves em uma matriz associativa e extrair as chaves:
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
Isso irá produzir
fonte
Outra opção para lidar com espaços em branco incorporados é delimitar nulos
printf
, fazer distinção com esort
, em seguida, usar um loop para empacotá-los de volta em uma matriz:input=(a b c "$(printf "d\ne")" b c "$(printf "d\ne")") output=() while read -rd $'' element do output+=("$element") done < <(printf "%s\0" "${input[@]}" | sort -uz)
No final disso,
input
eoutput
contêm os valores desejados (a ordem fornecida não é importante):$ printf "%q\n" "${input[@]}" a b c $'d\ne' b c $'d\ne' $ printf "%q\n" "${output[@]}" a b c $'d\ne'
fonte
Que tal essa variação?
printf '%s\n' "${ids[@]}" | sort -u
fonte
sorted_arr=($(printf '%s\n' "${ids[@]}" | sort -u)
.Tente isto para obter valores uniq para a primeira coluna no arquivo
awk -F, '{a[$1];}END{for (i in a)print i;}'
fonte
# Read a file into variable lines=$(cat /path/to/my/file) # Go through each line the file put in the variable, and assign it a variable called $line for line in $lines; do # Print the line echo $line # End the loop, then sort it (add -u to have unique lines) done | sort -u
fonte