Usando sed en un script de shell con variables multilínea

Usando sed en un script de shell con variables multilínea

Utilizo scripts de shell para configurar diferentes tipos de máquinas virtuales. A menudo, estos scripts incluyen variables multilínea que deben insertarse en archivos de configuración en determinadas posiciones mediante sed.

Si los creo así, todo está bien:

VAR="Config Line1\nConfig Line2\nConfig Line 3"
sed -i "/MatchingPattern/ a $VAR" somefile

Sin embargo, esto no hace que el script sea muy legible, especialmente porque los bloques de texto pueden ser bastante largos.

Si los escribo así:

VAR="Config Line1
Config Line2
Config Line 3"
sed -i "/MatchingPattern/ a $VAR" somefile

Recibo un error al ejecutar el script: sed: -e expression #1, char 31: unknown command:C''

¿Hay alguna manera de usarlo sedcon variables declaradas así?

Respuesta1

La sintaxis estándar del acomando es:

sed -e 'a\
first line\
  second line\
last line'

BSD (al menos FreeBSD y OS/X) sedelimina los espacios en blanco principales, ynecesita el -epara solucionar un error. GNU sedpermite mover la primera línea justo después de a\siempre que no esté vacía.

Entonces, necesitarías preprocesar la entrada:

VAR='content with
multiple lines
  some with lead blanks
or even backslash\'

preprocessed_VAR=$(printf '%s\n' "$VAR" |
  sed 's/\\/&&/g;s/^[[:blank:]]/\\&/;s/$/\\/')

sed -i -e "/MatchingPattern/a\\
${preprocessed_VAR%?}" somefile

(reemplazar -icon -i ''en FreeBSD o OS/X).

En GNU/Linux y con el shell GNU ( bash) o zsh, puedes hacer en su lugar:

sed -i '/MatchingPattern/r /dev/stdin' <<< "$VAR"

Eso funciona porque bashe zshimplementar here-strings con archivos temporales eliminados y /dev/stdin en Linux se implementa como un enlace simbólico al archivo (lo que significa que se puede abrir varias veces y cada vez, se abre al principio).

Aquí también puedes usar GNU awken su lugar:

(export VAR
gawk -i /usr/share/awk/inplace.awk '{print}; /MatchingPattern/{print ENVIRON["VAR"]}' somefile)

No utilice-i inplaceas gawkintenta cargar primero la inplaceextensión (como inplaceo inplace.awk) desde el directorio de trabajo actual, donde alguien podría haber colocado malware. La ruta de la inplaceextensión suministrada gawkpuede variar según el sistema; consulte el resultado degawk 'BEGIN{print ENVIRON["AWKPATH"]}'

O perl(de donde -iviene eso):

(export VAR
perl -pi -e '$_ .= "$ENV{VAR}\n" if /MatchingPattern/' somefile)

Respuesta2

El error se debe a que el sedcomando agregar anecesita una barra invertida después de lo real a:

Con GNU sed:

$ sed "/pattern/a\\$VAR" file.in >file.out

BSD sed, hasta donde yo sé, solo puede insertar una sola línea en un archivo con el acomando, a menos que las nuevas líneas se escapen correctamente.

Respuesta estrechamente relacionada sobre cómo resolver esto con BSD sed:http://unix.stackexchange.com/a/60322

información relacionada