¿Cómo puedo obtener valores únicos de una matriz en Bash?

Tengo casi la misma pregunta que aquí .

Tengo una matriz que contiene aa ab aa ac aa ad , etc. Ahora quiero seleccionar todos los elementos únicos de esta matriz. Pensamiento, esto sería simple con sort | uniq sort | uniq o con sort -u como mencionan en esa otra pregunta, pero nada cambió en la matriz … El código es:

 echo `echo "${ids[@]}" | sort | uniq` 

¿Qué estoy haciendo mal?

Un poco hacky, pero esto debería hacerlo:

 echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ' 

Para guardar los resultados únicos ordenados de nuevo en una matriz, haga la asignación de matriz :

 sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) 

Si su shell admite herestrings (debe bash ), puede ahorrar un proceso de echo alterándolo a:

 tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' ' 

Entrada:

 ids=(aa ab aa ac aa ad) 

Salida:

 aa ab ac ad 

Explicación:

  • "${ids[@]}" : syntax para trabajar con matrices de shell, ya sea que se use como parte de echo o de una herestring. La parte @ significa "todos los elementos en la matriz"
  • tr ' ' '\n' - Convierte todos los espacios en nuevas líneas. Debido a que su matriz es vista por shell como elementos en una sola línea, separados por espacios; y porque sort espera que la entrada esté en líneas separadas.
  • sort -u - ordenar y retener solo elementos únicos
  • tr '\n' ' ' - convierte las líneas nuevas que agregamos en espacios anteriores.
  • $(...) - Subsidencia de comando
  • Aparte: tr ' ' '\n' <<< "${ids[@]}" es una forma más eficiente de hacer: echo "${ids[@]}" | tr ' ' '\n' echo "${ids[@]}" | tr ' ' '\n'

Si está ejecutando Bash versión 4 o superior (que debería ser el caso en cualquier versión moderna de Linux), puede obtener valores de matriz únicos en bash mediante la creación de una nueva matriz asociativa que contiene cada uno de los valores de la matriz original. Algo como esto:

 $ 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 

Esto funciona porque en una matriz, cada tecla solo puede aparecer una vez. Cuando el bucle for llega al segundo valor de aa en a[2] , sobrescribe b[aa] que se configuró originalmente para a[0] .

Hacer cosas en nativo bash puede ser más rápido que usar tuberías y herramientas externas como sort y uniq .

Si los elementos de su matriz tienen un espacio en blanco o cualquier otro carácter especial de shell (y ¿puede estar seguro de que no?), Entonces, para capturarlos primero (y siempre debe hacerlo), exprese su matriz entre comillas dobles. por ejemplo, "${a[@]}" . Bash literalmente interpretará esto como “cada elemento de la matriz en un argumento separado”. Dentro de bash esto simplemente siempre funciona, siempre.

Luego, para obtener una matriz ordenada (y única), debemos convertirla en una clase de formato que comprendamos y poder convertirla de nuevo en elementos de matriz bash. Este es el mejor que he encontrado:

 eval a=($(printf "%q\n" "${a[@]}" | sort -u)) 

Desafortunadamente, esto falla en el caso especial de la matriz vacía, convirtiendo la matriz vacía en una matriz de 1 elemento vacío (porque printf tenía 0 argumentos pero aún se imprime como si tuviera un argumento vacío – ver explicación). Entonces tienes que atrapar eso en un si o algo así.

Explicación: El formato% q para printf “shell escapa” del argumento impreso, de tal manera que bash puede recuperarse en algo como eval! Debido a que cada elemento se imprime el shell escapado en su propia línea, el único separador entre elementos es la línea nueva, y la asignación de la matriz toma cada línea como un elemento, analizando los valores escapados en texto literal.

p.ej

 > a=("foo bar" baz) > printf "%q\n" "${a[@]}" 'foo bar' baz > printf "%q\n" '' 

La evaluación es necesaria para quitar el escape de cada valor que vuelve a la matriz.

Me doy cuenta de que esto ya fue respondido, pero apareció bastante alto en los resultados de búsqueda, y podría ayudar a alguien.

 printf "%s\n" "${IDS[@]}" | sort -u 

Ejemplo:

 ~> 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’ se puede usar para ordenar la salida de un for-loop:

 for i in ${ids[@]}; do echo $i; done | sort 

y eliminar duplicados con “-u”:

 for i in ${ids[@]}; do echo $i; done | sort -u 

Finalmente, puede sobrescribir su matriz con los elementos únicos:

 ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` ) 

este también mantendrá el orden:

 echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++' 

y para modificar la matriz original con los valores únicos:

 ARRAY=($(echo ${ARRAY[@]} | tr [:space:] '\n' | awk '!a[$0]++')) 

Para crear una nueva matriz que consista en valores únicos, asegúrese de que la matriz no esté vacía y realice una de las siguientes acciones:

Eliminar entradas duplicadas (con clasificación)

 readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u) 

Eliminar entradas duplicadas (sin ordenar)

 readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[$0]++') 

Advertencia: no intente hacer algo como NewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) ) . Rompe espacios

cat number.txt

 1 2 3 4 4 3 2 5 6 

Imprimir línea en la columna: cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}' cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}'

 1 2 3 4 4 3 2 5 6 

encuentre los registros duplicados: cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++' cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++'

 4 3 2 

Reemplazar registros duplicados: cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++' cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++'

 1 2 3 4 5 6 

Encuentra solo registros de cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i|"sort|uniq -u"} : cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i|"sort|uniq -u"} cat number.txt | awk 'BEGIN{FS=" "} {for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}

 1 5 6 

Si quiere una solución que solo utiliza bash internal, puede establecer los valores como claves en una matriz asociativa, y luego extraer las claves:

 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 

Esto producirá

 bar foo bar none 

Sin perder el orden original:

 uniques=($(tr ' ' '\n' <<<"${original[@]}" | awk '!u[$0]++' | tr '\n' ' ')) 

Intente esto para obtener valores uniq para la primera columna en el archivo

 awk -F, '{a[$1];}END{for (i in a)print i;}'