139 votes

Une façon propre d'écrire une chaîne complexe de plusieurs lignes dans une variable

J'ai besoin d'écrire un xml complexe dans une variable à l'intérieur d'un script bash. Le xml doit être lisible à l'intérieur du script de bash car c'est là que le fragment de xml va vivre, il n'est pas lu depuis un autre fichier ou une autre source.

Ma question est donc la suivante : si j'ai une longue chaîne de caractères que je veux rendre lisible par l'homme dans mon bash script, quelle est la meilleure façon de procéder ?

Idéalement, je veux :

  • pour ne pas avoir à échapper à un des caractères
  • faire en sorte qu'il s'interrompe sur plusieurs lignes pour le rendre lisible par l'homme
  • conserve son indentation

Cela peut-il être fait avec EOF ou autre, quelqu'un peut-il me donner un exemple ?

par exemple

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

0 votes

Je suis prêt à parier que vous allez juste déverser ces données dans un flux à nouveau. Pourquoi les stocker dans une variable alors que vous pourriez rendre les choses plus complexes et utiliser des flux ?

173voto

jason saldo Points 5036

Cela mettra votre texte dans votre variable sans avoir besoin d'échapper les guillemets. Elle traitera également les guillemets non équilibrés (apostrophes, c'est-à-dire ' ). Le fait de mettre des guillemets autour de la sentinelle (EOF) empêche le texte de subir une expansion des paramètres. Le site -d'' permet de lire plusieurs lignes (sans tenir compte des sauts de ligne). read est intégré à Bash et ne nécessite donc pas l'appel d'une commande externe telle que 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

0 votes

Je pense qu'éviter cat n'est pas un avantage ici. Au moins, vous économisez quelques caractères en l'utilisant ;)

6 votes

cat est une commande externe. Ne pas l'utiliser évite de le faire. De plus, certains ont pour philosophie que si vous utilisez cat avec moins de deux arguments, "vous le faites mal" (ce qui est distinct de "l'utilisation inutile de cat ").

0 votes

Cela ne fonctionne pas pour moi à moins que je mette un espace entre -d y '' -- vraisemblablement parce qu'il ne réalise pas qu'il y a une chaîne vide après -d sauf si c'est techniquement un paramètre séparé. Cela peut également dépendre de la version de bash que vous avez (j'utilise 3.2.17 sous Mac OS X v10.5.8).

43voto

Crippeoblade Points 1301

Vous y êtes presque. Soit vous utilisez chat pour l'assemblage de votre chaîne ou vous citez toute la chaîne (auquel cas vous devrez échapper les guillemets à l'intérieur de votre chaîne) :

#!/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}"

1 votes

Malheureusement, l'apostrophe dans "Raphael's" fait que la première ne fonctionne pas.

0 votes

Les deux affectations me conviennent éventuellement. Le guillemet simple dans VAR1 ne devrait pas être un problème (du moins pas pour bash). Peut-être avez-vous été induit en erreur par la coloration syntaxique ?

2 votes

Cela fonctionne dans un script, mais pas à une invite Bash. Désolé de ne pas avoir été plus clair.

22voto

Jan Points 31
#!/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}"

Cela devrait fonctionner correctement dans l'environnement Bourne Shell.

4 votes

+1 cette solution permet la substitution de variables comme ${foo}

0 votes

Avantage : compatible avec sh. Inconvénient : les backticks sont dépréciés/découragés dans bash. Maintenant, si je devais choisir entre sh et bash...

2 votes

Depuis quand les backticks sont-ils dépréciés/découragés ? juste par curiosité

6voto

F. Hauri Points 969

Encore une autre façon de faire la même chose...

J'aime utiliser des variables et des <<- qui abandonnent tabulation au début de chaque ligne pour permettre l'indentation du script :

#!/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

avertissement il n'y a pas de espace vide antes de eof mais seulement tabulation .

<?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>

Quelques explications :

  • lecture complète du fichier de carte ici le document dans un tableau.
  • la syntaxe "${Pattern[*]}" transformez ce tableau en une chaîne de caractères.
  • J'utilise IFS=";" parce qu'il n'y a pas ; dans les chaînes de caractères requises
  • La syntaxe while IFS=";" read file ... prévenir IFS à modifier pour le reste du script. Dans ce cas, seul read utilisez la version modifiée IFS .
  • pas de fourche.

0 votes

Notez que mapfile nécessite Bash 4 ou plus. Et la syntaxe "${Pattern[*]}" convertit le tableau en une chaîne de caractères entre guillemets (comme indiqué dans l'exemple de code).

0 votes

Oui, bash 4 était très nouveau lorsque cette question a été posée.

3voto

Chris Johnson Points 763

Il y a trop de cas de coin dans beaucoup des autres réponses.

Pour être absolument sûr qu'il n'y a pas de problèmes avec les espaces, les tabulations, les IFS etc., une meilleure approche est d'utiliser la construction "heredoc", mais d'encoder le contenu de l'heredoc en utilisant uuencode comme expliqué ici :

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

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X