Tengo una cadena que tiene palabras duplicadas. Me gustaría mostrar solo las palabras únicas. la cadena es:
variable="alpha bravo charlie alpha delta echo charlie"Conozco varias herramientas que pueden hacer esto juntas. Esto es lo que descubrí:
echo $variable | tr " " "\n" | sort -u | tr "\n" " "¿Cuál es una forma más efectiva de hacer esto?
La siguiente expansión de parámetros de shell sustituirá los espacios por líneas nuevas y luego pasará los resultados a la utilidad de ordenación para devolver solo las palabras únicas.
$ echo -e "${variable// /\\n}" | sort -u alpha bravo charlie delta echoEsto tiene el efecto secundario de clasificar sus palabras, ya que las utilidades sort y uniq requieren que se clasifique la entrada para detectar duplicados. Si eso no es lo que desea, también publiqué una solución de Ruby que conserva el orden original de las palabras.
Si, como señaló un comentarista, está tratando de volver a ensamblar sus palabras únicas en una sola línea, puede usar la sustitución de comandos para hacer esto. Por ejemplo:
$ echo $(echo -e "${variable// /\\n}" | sort -u) alpha bravo charlie delta echoLa falta de comillas en torno a la sustitución del comando es intencional. Si lo cita, las líneas nuevas se conservarán porque Bash no dividirá las palabras . Sin comillas, el shell devolverá los resultados como una sola línea, por poco intuitivo que parezca.
Puede usar xargs :
echo "$variable" | xargs -n 1 | sort -u | xargsNota: esta solución asume que todas las palabras únicas deben generarse en el orden en que se encuentran en la entrada . Por el contrario, el intento de solución del propio OP genera una lista ordenada de palabras únicas.
Una solución simple solo de Awk (compatible con POSIX) que es eficiente al evitar una canalización (que invariablemente involucra subcapas).
awk -v RS=' ' '{ if (!seen[$1]++) { printf "%s%s",sep,$1; sep=" " } }' <<<"$variable" # The above prints without a trailing \n, as in the OP's own solution. # To add a trailing newline, append `END { print }` to the end # of the Awk script. Tenga en cuenta cómo $variable está entre comillas dobles para evitar que se produzcan expansiones de shell accidentales, en particular la expansión del nombre de la ruta (globbing), y cómo se proporciona a Awk a través de una cadena aquí ( <<< ).
-v RS=' ' le dice a Awk que divida la entrada en registros por un solo espacio.
$0 , el registro completo, sino $1 , el primer campo del registro, que tiene el salto de línea eliminado debido al comportamiento predeterminado de división de campos de Awk. seen[$1]++ es un modismo común de Awk que crea una entrada para $1 , la palabra de entrada, en una matriz asociativa seen , si aún no existe, o incrementa su recuento de ocurrencias.
!seen[$0]++ por lo tanto, solo devuelve verdadero para la primera aparición de una palabra dada (donde seen[$0] es implícitamente cero/la cadena vacía; el ++ es un incremento posterior y, por lo tanto, no tiene efecto hasta después de evaluar la condición)
{printf "%s%s",sep,$1; sep=" "} imprime la palabra en cuestión $1 , precedida por el separador sep , que es implícitamente la cadena vacía para la primera palabra, pero un solo espacio para las palabras posteriores, debido a que se establece sep en " " inmediatamente después.
Aquí hay una variante más flexible que maneja cualquier espacio en blanco entre las palabras de entrada ; funciona con GNU Awk y Mawk [1] :
awk -v RS='[[:space:]]+' '{if (!seen[$0]++){printf "%s%s",sep,$0; sep=" "}}' <<<"$variable"-v RS='[[:space:]]s+' le dice a Awk que divida la entrada en registros por cualquier combinación de espacios, tabulaciones y saltos de línea. [1] Desafortunadamente, BSD/OSX Awk (en estricto cumplimiento con la especificación POSIX ), no admite el uso de expresiones regulares o incluso literales de varios caracteres como RS , el separador de registro de entrada.