Pregunta Forma limpia de escribir cadenas complejas de varias líneas en una variable


Necesito escribir algún xml complejo en una variable dentro de un script bash. El xml debe ser legible dentro del script bash ya que aquí es donde el fragmento xml vivirá, no se está leyendo desde otro archivo o fuente.

Entonces, mi pregunta es esta: si tengo una cadena larga que quiero que sea legible para los humanos dentro de mi script de bash, ¿cuál es la mejor manera de hacerlo?

Idealmente quiero:

  • Para no tener que escapar de ninguno de los personajes.
  • hacer que se rompa en varias líneas para que sea legible por humanos
  • mantener su sangría

¿Se puede hacer esto con EOF o algo? ¿Podría alguien darme un ejemplo?

p.ej.

String = <<EOF
 <?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF

94
2017-10-08 10:54


origen


Estoy dispuesto a apostar a que simplemente vas a volcar esos datos en una transmisión nuevamente. ¿Por qué almacenarlo en una variable cuando podrías hacer las cosas más complejas y usar flujos? - Zenexer
mira esto también Cadena multilínea con espacio adicional (sangría preservada) - Yibo Yang


Respuestas:


Esto pondrá su texto en su variable sin necesidad de escapar de las comillas. También manejará citas desequilibradas (apóstrofes, es decir, '). Poner comillas alrededor del centinela (EOF) evita que el texto sufra expansión de parámetros. los -d'' hace que lea múltiples líneas (ignorar nuevas líneas). read es un Bash incorporado, por lo que no requiere llamar a un comando externo como cat.

IFS='' read -r -d '' String <<"EOF"
<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
EOF

123
2017-10-08 12:37



+1 por evitar cat. - James Sneeringer
cat Es un comando externo. No usarlo ahorra haciendo eso. Además, algunos tienen la filosofía de que si usas cat con menos de dos argumentos, "no lo pienses bien" (lo que es distinto del "uso inútil de cat"). - Dennis Williamson
y nunca sangre el segundo EOF ... (hay varias tablas en la cabeza involucrada) - IljaBek
Traté de usar la declaración anterior mientras set -e. Parece read Siempre devuelve un valor distinto de cero. Puedes espesar este comportamiento usando ! read -d ....... - krissi
Y si estás utilizando esta multilínea. String variable para escribir en un archivo, coloque la variable alrededor de "CITAS" como echo "${String}" > /tmp/multiline_file.txt o echo "${String}" | tee /tmp/multiline_file.txt. Me llevó más de una hora encontrar eso. - Aditya


Has estado casi allí. O bien usas gato para el ensamblaje de su cadena o usted cita la cadena completa (en cuyo caso, tendría que escapar de las comillas dentro de su cadena):

#!/bin/sh
VAR1=$(cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
)

VAR2="<?xml version=\"1.0\" encoding='UTF-8'?>
<painting>
  <img src=\"madonna.jpg\" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's \"Foligno\" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>"

echo "${VAR1}"
echo "${VAR2}"

25
2017-10-08 11:14



Desafortunadamente, el apóstrofe en "Rafael" hace que el primero no funcione. - Dennis Williamson
Ambas tareas funcionan para mí eventualmente. La comilla simple en VAR1 no debería ser un problema (al menos no para bash). Tal vez usted ha sido engañado por el resaltado de sintaxis? - joschi
Funciona en una secuencia de comandos, pero no en un indicador de Bash. Lo siento por no ser más claro. - Dennis Williamson
Es mejor citar EOF como 'EOF' o "EOF"De lo contrario, se analizarán las variables de shell. - Stanislav German-Evtushenko


#!/bin/sh

VAR1=`cat <<EOF
<?xml version="1.0" encoding='UTF-8'?>
<painting>
  <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
  <caption>This is Raphael's "Foligno" Madonna, painted in
  <date>1511</date>-<date>1512</date>.</caption>
</painting>
EOF
`
echo "VAR1: ${VAR1}"

Esto debería funcionar bien dentro del entorno de shell Bourne


9
2017-09-05 15:54



+1 esta solución permite la sustitución de variables como $ {foo} - Offirmo
Al revés: sh-compatible. Desventaja: los backticks son obsoletos / desalentados en bash. Ahora si tuviera que elegir entre sh y bash ... - Zenexer
¿Desde cuándo están en desuso / desanimados los backticks? Sólo curioso - Alexander Mills


Otra forma de hacer lo mismo ...

Me gusta usar variables y especiales. <<- quien cae tabulación al comienzo de cada línea para permitir la sangría del guión:

#!/bin/bash

mapfile Pattern <<-eof
        <?xml version="1.0" encoding='UTF-8'?>
        <painting>
          <img src="%s" alt='%s'/>
          <caption>%s, painted in
          <date>%s</date>-<date>%s</date>.</caption>
        </painting>
        eof

while IFS=";" read file alt caption start end ;do
    printf "${Pattern[*]}" "$file" "$alt" "$caption" "$start" "$end"
  done <<-eof
        madonna.jpg;Foligno Madonna, by Raphael;This is Raphael's "Foligno" Madonna;1511;1512
        eof

advertencia: no hay espacio en blanco antes de eof pero sólo tabulación.

<?xml version="1.0" encoding='UTF-8'?>
 <painting>
   <img src="madonna.jpg" alt='Foligno Madonna, by Raphael'/>
   <caption>This is Raphael's "Foligno" Madonna, painted in
   <date>1511</date>-<date>1512</date>.</caption>
 </painting>
Algunas explicaciones:
  • Mapfile leer todo aquí documento en una matriz.
  • la sintaxis "${Pattern[*]}" hacer esta matriz en una cadena.
  • yo suelo IFS=";" porque no hay ; en cadenas necesarias
  • La sintaxis while IFS=";" read file ... evitar IFS para ser modificado para el resto del script. En esto, solo read usa el modificado IFS.
  • sin tenedor

4
2017-07-22 13:45



Tenga en cuenta que mapfile requiere Bash 4 o superior. Y la sintaxis "${Pattern[*]}" convierte la matriz en una cadena cuando entre comillas (como se muestra en el código de ejemplo). - Dennis Williamson
Sí, bash 4 fue muy nuevo cuando esta pregunta fue hecha - F. Hauri


Hay demasiados casos de esquina en muchas de las otras respuestas.

Para estar absolutamente seguro de que no hay problemas con los espacios, pestañas, IFS, etc., un mejor enfoque es utilizar el constructo "heredoc", pero codificar los contenidos del heredoc usando uuencode como se explica aquí:

https://stackoverflow.com/questions/6896025/#11379627.


2
2018-05-17 09:10