Usando la sustitución de parámetros en una matriz Bash

Tengo file.txt que necesito leer en una matriz Bash. Luego, necesito eliminar los espacios, las comillas dobles y todo menos la primera coma en cada entrada . Esto es lo lejos que he llegado:

$ cat file.txt 10,this 2 0 , i s 30,"all" 40,I 50,n,e,e,d,2 60",s e,e" $ cat script.sh #!/bin/bash readarray -t ARRAY<$1 ARRAY=( "${ARRAY[@]// /}" ) ARRAY=( "${ARRAY[@]//\"/}" ) for ELEMENT in "${ARRAY[@]}";do echo "|ELEMENT|$ELEMENT|" done $ ./script.sh file.txt |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,n,e,e,d,2| |ELEMENT|60,se,e| 

Lo cual funciona muy bien excepto por la situación de la coma. Soy consciente de que hay varias formas de desollar a este gato, pero debido a la secuencia de comandos más grande del que forma parte, realmente me gustaría usar la sustitución de parámetros para llegar aquí:

|ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see| 

¿Es esto posible mediante la sustitución de parámetros?

Comentarios

  • ¿Hay alguna razón por la que deba mantener el texto en una matriz, y por qué no puedes ‘ dejar, por ejemplo, awk o sed ¿procesan los datos?
  • @Jeff – El bucle sobre la matriz será un pesadilla para implementar en el script más grande en el que ‘ estoy trabajando.
  • @JonRed No ‘ no sé qué que está haciendo, por lo que ‘ es completamente posible que no tenga otra opción en el asunto, pero en general, cuando se encuentra haciendo acrobacias de cuerdas tan complejas en el caparazón, que ‘ es una muy buena indicación de que debería utilizar un lenguaje de programación real. El shell no está diseñado como un lenguaje de programación, y aunque se puede usar como tal, realmente no es ‘ t una buena idea para cosas más complejas. Le recomiendo encarecidamente que considere cambiar a perl o python o cualquier otro lenguaje de programación.
  • @terdon Es ‘ s gracioso, acabo de decir casi lo exacto lo mismo a mi colega antes de leer esta publicación. Básicamente dije que esta es la versión final de este script y que cualquier requisito adicional necesitará reescribirlo en Perl. Así que sí, definitivamente estoy de acuerdo

Responder

Quitaría lo que necesita quitar usando sed antes de cargar en la matriz (también tenga en cuenta los nombres de las variables en minúsculas, en general es mejor evitar las variables en mayúsculas en los scripts de shell):

#!/bin/bash readarray -t array< <(sed "s/"//g; s/ *//g; s/,/"/; s/,//g; s/"/,/" "$1") for element in "${array[@]}";do echo "|ELEMENT|$element|" done 

Esto produce la siguiente salida en su archivo de ejemplo:

$ foo.sh file |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see| 

Si realmente debe usar el parámetro sustitución, intente algo como esto:

#!/bin/bash readarray -t array< "$1" array=( "${array[@]// /}" ) array=( "${array[@]//\"/}" ) array=( "${array[@]/,/\"}" ) array=( "${array[@]//,/}" ) array=( "${array[@]/\"/,}" ) for element in "${array[@]}"; do echo "|ELEMENT|$element|" done 

Comentarios

  • @JonRed Agregué una versión con parámetro sustitución pero es ‘ s compleja, engorrosa y fea. Hacer este tipo de cosas en el shell rara vez es una buena idea.
  • Tenga en cuenta que si ‘ ha eliminado tanto los espacios como las comillas dobles, estos caracteres estarán disponibles para usar en lugar de su RANDOMTEXTTHATWILLNEVERBEINTHEFILE.
  • @Kusalananda sí, acabo de leer su respuesta. ¡Debería haber pensado en eso! Gracias 🙂
  • Responde directamente la pregunta, ilustra por qué mi solución preferida no es ‘ t ideal y proporciona la alternativa más viable. Usted gana, la mejor respuesta.

Respuesta

Por lo que puedo ver, no es necesario léelo en una matriz bash para crear esa salida:

$ sed "s/[ "]//g; s/,/ /; s/,//g; s/ /,/; s/.*/|ELEMENT|&|/" <file |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see| 

La sed expresión elimina espacios y comillas dobles, reemplaza la primera coma con un espacio (no hay otros espacios en la cadena en este punto), elimina todas las demás comas, restaura la primera coma y antepone y agrega los datos adicionales .

Alternativamente, con GNU sed:

sed "s/[ "]//g; s/,//2g; s/.*/|ELEMENT|&|/" <file 

(estándar sed no admite la combinación de 2 y g como indicadores para s comando).

Comentarios

  • con GNU sed, puede usar 's/,//2g para eliminar las comas, comenzando con el segundo
  • Y, los últimos 2 s /// comandos pueden ser s/.*/|ELEMENT|&|/ pero eso puede ser más esfuerzo para sed.
  • @glennjackman Posiblemente, pero se ve bastante ordenado.
  • Sí, esto es parte de un script más grande. La matriz es necesaria, no solo para la salida. De ahí mi interés en la sustitución de parámetros. Podría recorrer la matriz con esto, pero será una pesadilla de implementar. Terndon proporcionó una solución sin bucles utilizando sed a la que ‘ probablemente recurriré si la sustitución de parámetros no es válida.
  • Si no estaba ‘ t vinculado al uso de una matriz, sin embargo, esta sería la mejor solución.

Respuesta

ELEMENT="50,n,e,e,d,2" IFS=, read -r first rest <<<"$ELEMENT" printf "%s,%s\n" "$first" "${rest//,/}" 
50,need2 

Olvídese del hábito de usar nombres de variables ALLCAPS. Eventualmente chocará con una variable crucial del «sistema» como PATH y romperá su código.

Comentarios

  • Sin sustitución de parámetros. PERO, no sabía que los nombres de las variables ALLCAPS eran un mal hábito en Bash. Tienes un buen punto, uno que definitivamente confirma una búsqueda superficial en Google. ¡Gracias por mejorar mi estilo! 🙂
  • Yo ‘ he respondido preguntas en las que la persona escribió PATH=something; ls $PATH y luego se preguntó sobre el ls: command not found error.
  • Hay casi un centenar de variables integradas que se nombran en mayúsculas (haga clic en esta página de manual enlace ) para ver …

Respuesta

[Esta es esencialmente una versión más completa versión de respuesta de glenn jackmann ]

Construyendo una matriz asociativa a partir de la clave y el valor despojados, usando la primera coma como separador:

declare -A arr while IFS=, read -r k v; do arr["${k//[ \"]}"]="${v//[ ,\"]}"; done < file.txt for k in "${!arr[@]}"; do printf "|ELEMENT|%s,%s|\n" "$k" "${arr[$k]}" done |ELEMENT|20,is| |ELEMENT|10,this| |ELEMENT|50,need2| |ELEMENT|40,I| |ELEMENT|60,see| |ELEMENT|30,all| 

Respuesta

Puede recorrer la matriz y usar una variable intermedia:

for((i=0; i < "${#ARRAY[@]}"; i++)) do rest="${ARRAY[i]#*,}" ARRAY[i]="${ARRAY[i]%%,*}","${rest//,/}" done 

Esto asigna a rest la parte después de la primera coma; luego concatenamos tres piezas de nuevo en el original variable:

  • la parte antes de la primera coma
  • una coma
  • el reemplazo en rest de cada coma sin nada

Comentarios

  • Este fue mi primer pensamiento y es bastante simple para el ejemplo, pero esto es parte de un script más grande donde la matriz es masiva y hay ‘ s ya bucles y sería una cosa completa. Esto definitivamente funcionaría, pero sería muy complicado de implementar en el proyecto más grande en el que ‘ estoy trabajando.
  • Bastante justo; Intenté responder dentro de las limitaciones (solo expansión de parámetros).

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *