Nota: Gracias a Jeff Schaller y Steeldriver. Pero como ninguno de los dos publicó como respuesta, no estoy seguro de cómo marcarlo como resuelto. Ahora entiendo mejor las tuberías/subcapas. Estoy bastante seguro de que alguna vez supe esto, pero ha pasado mucho tiempo desde que probé algo complejo en bash.
Tanto asignando el resultado filtrado de awk a una variable comosustitución de procesostrabajó para mi. Mi código final para leer líneas únicas sin clasificar de stdin
:
while read -r FILE
do
...
done < <(awk '!x[$0]++')
Más lecturas ensustitución de procesospara aquellos que encuentran esta pregunta y buscan una solución a un problema similar.
PREGUNTA ORIGINAL:
Busqué en el sitio, pero no encuentro una respuesta a mi problema.
Estoy creando una matriz a partir de la entrada estándar y necesito filtrar líneas únicas. Para hacer esto, estoy usando awk '!x[$0]++'
lo que he leído es una abreviatura de:
awk 'BEGIN { while (getline s) { if (!seen[s]) print s; seen[s]=1 } }'
.
El filtro funciona como se desea, pero el problema es que la matriz resultante del while read
bucle está vacía.
Por ejemplo (usando $list
como sustituto de stdin
):
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
while read -r line; do
array[count++]=$line
done <<< "$list"
echo "array length = ${#array[@]}"
counter=0
while [ $counter -lt ${#array[@]} ]; do
echo ${array[counter++]}
done
produce:
array length = 5
red apple
yellow banana
purple grape
orange orange
yellow banana
Pero filtrando $list
con awk:
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
awk '!x[$0]++' <<< "$list" | while read -r line; do
array[count++]=$line
done
echo "array length = ${#array[@]}"
counter=0
while [ $counter -lt ${#array[@]} ]; do
echo ${array[counter++]}
done
produce:
array length = 0
Pero el resultado de awk '!x[$0]++' <<< "$list"
parece estar bien:
red apple
yellow banana
purple grape
orange orange
Intenté examinar cada línea del while read
bucle:
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
i=0
awk '!x[$0]++' <<< "$list" | while read -r line; do
echo "line[$i] = $line"
let i=i+1
done
y parece bien:
line[0] = red apple
line[1] = yellow banana
line[2] = purple grape
line[3] = orange orange
¿Que me estoy perdiendo aqui?
Por si es importante, estoy usando bash 3.2.57:
GNU bash, versión 3.2.57(1) (x86_64-apple-darwin15) Copyright (C) 2007 Free Software Foundation, Inc.
Respuesta1
awk '!x[$0]++' <<< "$lista" |mientras lee -r línea; hacer formación[cuenta++]=$línea hecho
Elarray
(itálico) en este caso es parte delsubshell
(atrevido).
El $line
y $array
tiene un valor.mientras quela subcapa está viva, por así decirlo.
Una vez que finaliza la subcapa, también conocida como muere, se restaura el entorno principal (generador). Esto incluye la eliminación de cualquier variable establecida en el subnivel.
En este caso:
remoto,$array
remoto.$line
Prueba esto:
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
awk '!x[$0]++' <<< "$list" | while read -r line; do
array[count++]=$line
printf "array[%d] { %s\n" ${#array[@]} # array[num_of_elements] {
printf " %s\n" "${array[@]}" # elements
printf "}\n" # } end of array
done
printf "\n[ %s ]\n\n" "END OF SUBSHELL (PIPE)"
printf "array[%d] {\n" ${#array[@]}
printf " %s\n" "${array[@]}"
printf "}\n"
Rendimientos:
array[1] {
red apple
}
array[2] {
red apple
yellow banana
}
array[3] {
red apple
yellow banana
purple grape
}
array[4] {
red apple
yellow banana
purple grape
orange orange
}
[ END OF SUBSHELL (PIPE) ]
array[0] {
}
O según el manual.
Podemos empezar conTuberías
[…] Cada comando en una canalización se ejecuta por su cuentasubcapa(verEntorno de ejecución de comandos). […]
Y elEntorno de ejecución de comandosamplía la aventura de la siguiente manera:
[…] Un comando invocado en esteambiente separado no puedoafectar el entorno de ejecución del shell.
La sustitución de comandos, los comandos agrupados con paréntesis y los comandos asincrónicos se invocan en un entorno de subshell que es un duplicado del entorno de shell, excepto que las trampas detectadas por el shell se restablecen a los valores que el shell heredó de su padre en el momento de la invocación. Los comandos integrados que se invocan como parte de una canalización también se ejecutan en un entorno de subshell.Los cambios realizados en el entorno del subshell no pueden afectar el entorno de ejecución del shell.[…]
No puede afectar: por lo tanto no puede establecer.
Sin embargo, podemos redirigir y hacer algo en la dirección de:
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
while read -r line; do
arr[count++]=$line
done <<<"$(awk '!x[$0]++' <<< "$list")"
echo "arr length = ${#arr[@]}"
count=0
while [[ $count -lt ${#arr[@]} ]]; do
echo ${arr[count++]}
done
Respuesta2
Algunas soluciones a tu problemasin el bucle
# use bash's mapfile with process substitution
mapfile -t arr < <( awk '!x[$0]++' <<<"$list" )
# use array assignment syntax (at least bash, ksh, zsh)
# of a command-substituted value split at newline only
# and (if the data can contain globs) globbing disabled
set -f; IFS='\n' arr=( $( awk '!x[$0]++' <<<"$list" ) ); set +f