web-dev-qa-db-fra.com

Comment assigner une valeur heredoc à une variable dans Bash?

J'ai cette chaîne multi-lignes (guillemets inclus):

abc'asdf"
$(dont-execute-this)
foo"bar"''

Comment pourrais-je l'assigner à une variable utilisant un heredoc dans Bash?

Je dois préserver les nouvelles lignes.

Je ne veux pas échapper aux caractères de la chaîne, ce serait agaçant ...

290
Neil

Vous pouvez éviter une utilisation inutile de cat et gérer mieux les guillemets incompatibles avec ceci:

$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

Si vous ne citez pas la variable lorsque vous y répondez, les nouvelles lignes sont perdues. Citer cela les préserve:

$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

Si vous souhaitez utiliser l'indentation pour plus de lisibilité dans le code source, utilisez un tiret après les caractères inférieurs. L'indentation doit être faite en utilisant seulement des tabulations (pas d'espaces).

$ read -r -d '' VAR <<-'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
    EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

Si, au lieu de cela, vous souhaitez conserver les onglets dans le contenu de la variable résultante, vous devez supprimer l'onglet de IFS. Le marqueur de terminal pour le doc ici (EOF) ne doit pas être mis en retrait.

$ IFS='' read -r -d '' VAR <<'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
EOF
$ echo "$VAR"
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''

Les onglets peuvent être insérés en ligne de commande en appuyant sur Ctrl-VTab. Si vous utilisez un éditeur, cela peut également fonctionner, selon le cas, ou vous devrez peut-être désactiver la fonctionnalité qui convertit automatiquement les onglets en espaces.

442
Dennis Williamson

Utilisez $ () pour affecter le résultat de cat à votre variable comme ceci:

VAR=$(cat <<'END_HEREDOC'
abc'asdf"
$(dont-execute-this)
foo"bar"''
END_HEREDOC
)

# this will echo variable with new lines intact
echo "$VAR"
# this will echo variable without new lines (changed to space character)
echo $VAR

Assurez-vous de délimiter le début de END_HEREDOC avec des guillemets simples.

Notez que la fin du délimiteur heredoc END_HEREDOC doit être seul sur la ligne (la parenthèse fin se trouvant donc à la ligne suivante).

Merci à @ephemient pour la réponse.

172
Neil

ceci est une variante de la méthode de Dennis, semble plus élégante dans les scripts.

définition de la fonction:

define(){ IFS='\n' read -r -d '' ${1} || true; }

usage:

define VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

echo "$VAR"

prendre plaisir

p.s. fait un 'read loop' version pour les shells qui ne supportent pas read -d. devrait fonctionner avec set -eu et des backticks non appariés , mais pas très bien testés:

define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }
73
ttt
VAR=<<END
abc
END

ne fonctionne pas parce que vous redirigez stdin vers quelque chose qui ne le concerne pas, à savoir l'affectation

export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A

fonctionne, mais il y a un back-tic là-bas qui peut vous empêcher de l'utiliser. En outre, vous devriez vraiment éviter d'utiliser des backticks, il est préférable d'utiliser la notation de substitution de commande $(..).

export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A
26
l0st3d

Ajouter un commentaire ici comme réponse car je n'ai pas assez de points de rep pour commenter le texte de votre question.

Il n'y a toujours pas de solution qui préserve les nouvelles lignes.

Ce n'est pas vrai - vous êtes probablement simplement induit en erreur par le comportement de l'écho:

echo $VAR # strips newlines

echo "$VAR" # preserves newlines

25
patspam

Un tableau est une variable, donc dans ce cas, mapfile fonctionnera

mapfile y <<z
abc'asdf"
$(dont-execute-this)
foo"bar"''
z

Ensuite, vous pouvez imprimer comme ça

printf %s "${y[@]}"
8
Steven Penny

Dérivation Réponse de Neil , vous n'avez souvent pas besoin de var, vous pouvez utiliser une fonction de la même manière qu'une variable et elle est beaucoup plus facile à lire que les solutions inline ou read.

$ complex_message() {
  cat <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
}

$ echo "This is a $(complex_message)"
This is a abc'asdf"
$(dont-execute-this)
foo"bar"''
8
dimo414

assigner une valeur heredoc à une variable

VAR="$(cat <<'VAREOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
VAREOF
)"

utilisé comme argument d'une commande

echo "$(cat <<'SQLEOF'
xxx''xxx'xxx'xx  123123    123123
abc'asdf"
$(dont-execute-this)
foo"bar"''
SQLEOF
)"
3
bronze man

Je me suis retrouvé à lire une chaîne contenant NULL. Voici donc une solution permettant de lire n'importe quoi que vous lui lancez. Bien que si vous avez réellement affaire à NULL, vous devrez vous en occuper au niveau hexadécimal.

$ cat> read.dd.sh

read.dd() {
     buf= 
     while read; do
        buf+=$REPLY
     done < <( dd bs=1 2>/dev/null | xxd -p )

     printf -v REPLY '%b' $( sed 's/../ \\\x&/g' <<< $buf )
}

Preuve:

$ . read.dd.sh
$ read.dd < read.dd.sh
$ echo -n "$REPLY" > read.dd.sh.copy
$ diff read.dd.sh read.dd.sh.copy || echo "File are different"
$ 

Exemple HEREDOC (avec ^ J, ^ M, ^ I):

$ read.dd <<'HEREDOC'
>       (TAB)
>       (SPACES)
(^J)^M(^M)
> DONE
>
> HEREDOC

$ declare -p REPLY
declare -- REPLY="  (TAB)
      (SPACES)
(^M)
DONE

"

$ declare -p REPLY | xxd
0000000: 6465 636c 6172 6520 2d2d 2052 4550 4c59  declare -- REPLY
0000010: 3d22 0928 5441 4229 0a20 2020 2020 2028  =".(TAB).      (
0000020: 5350 4143 4553 290a 285e 4a29 0d28 5e4d  SPACES).(^J).(^M
0000030: 290a 444f 4e45 0a0a 220a                 ).DONE
1
Orwellophile

Grâce à dimo414's answer , cela montre le fonctionnement de cette excellente solution et montre que vous pouvez facilement inclure des guillemets et des variables dans le texte:

exemple de sortie

$ ./test.sh

The text from the example function is:
  Welcome dev: Would you "like" to know how many 'files' there are in /tmp?

  There are "      38" files in /tmp, according to the "wc" command

test.sh

#!/bin/bash

function text1()
{
  COUNT=$(\ls /tmp | wc -l)
cat <<EOF

  $1 Would you "like" to know how many 'files' there are in /tmp?

  There are "$COUNT" files in /tmp, according to the "wc" command

EOF
}

function main()
{
  OUT=$(text1 "Welcome dev:")
  echo "The text from the example function is: $OUT"
}

main
0
Brad Parks