Comment attribuer une valeur heredoc à une variable dans Bash?

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

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

Comment l'attribuer à une variable en utilisant un heredoc dans Bash?

Je dois préserver les nouvelles lignes.

Je ne veux pas échapper aux caractères dans la chaîne, ce serait ennuyeux...

274
demandé sur Smi 2009-07-22 23:43:31

11 réponses

Vous pouvez éviter une utilisation inutile de cat et mieux gérer 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 la faites écho, les sauts de ligne sont perdus. Citer les préserve:

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

Si vous voulez utiliser l'indentation pour la lisibilité dans le code source, utilisez un tiret après les less-thans. L'indentation doit être effectuée en utilisant uniquement des onglets (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, à la place, vous voulez conserver les onglets dans le contenu de la variable résultante, vous devez supprimer l'onglet de IFS. Le marqueur terminal pour le doc ici (EOF) ne doit pas être indenté.

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

Onglets peuvent être insérés à la ligne de commande en appuyant sur Ctrl-V Onglet. Si vous utilisez un éditeur, en fonction de celui qui, qui peut également fonctionner ou vous devrez peut-être désactiver la fonction qui convertit automatiquement les tabulations en espaces.

399
répondu Dennis Williamson 2018-01-22 16:25:44

Utiliser $() pour affecter la sortie 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 end_heredoc de départ avec des guillemets simples.

Notez que la fin du délimiteur heredoc END_HEREDOC doit être seule sur la ligne (donc la fin de la parenthèse est sur la ligne suivante).

Grâce à @ephemient pour la réponse.

138
répondu Neil 2018-02-05 16:01:29

Ceci est une variation de la méthode Dennis, semble plus élégant dans les scripts.

Fonction définition:

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

Utilisation:

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

echo "$VAR"

Profitez

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

define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }
71
répondu ttt 2017-10-04 15:49:27
VAR=<<END
abc
END

Ne fonctionne pas parce que vous redirigez stdin vers quelque chose qui ne s'en soucie pas, à savoir l'affectation

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

Fonctionne, mais il y a un back-tic là-dedans qui peut vous empêcher d'utiliser ceci. 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
25
répondu l0st3d 2014-01-12 06:29:06

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

Il n'y a toujours pas de solution qui préserve les retours à la ligne.

Ce n'est pas vrai - vous êtes probablement juste induit en erreur par le comportement d'echo:

echo $VAR # strips newlines

echo "$VAR" # preserves newlines

24
répondu patspam 2013-08-08 12:58:55

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

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

Ensuite, vous pouvez imprimer comme ceci

printf %s "${y[@]}"
6
répondu Steven Penny 2014-12-25 19:30:44

Brancher la réponse de Neil , vous n'avez souvent pas besoin d'un var, vous pouvez utiliser une fonction de la même manière qu'une variable et c'est beaucoup plus facile à lire que les solutions en ligne ou basées sur 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"''
5
répondu dimo414 2017-05-23 11:54:59

Attribuez 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
)"
4
répondu bronze man 2013-07-28 04:11:04

Je me suis retrouvé à lire une chaîne avec NULL dedans, alors voici une solution qui va lire Tout ce que Vous lancez dessus. Bien que si vous traitez réellement avec NULL, vous devrez traiter cela 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
répondu Orwellophile 2012-06-04 04:41:29

Grâce à la réponse de dimo414 , Cela montre comment fonctionne sa bonne solution, et montre que vous pouvez aussi facilement avoir 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
répondu Brad Parks 2017-05-23 11:54:59
$TEST="ok"
read MYTEXT <<EOT
this bash trick
should preserve
newlines $TEST
long live perl
EOT
echo -e $MYTEXT
-7
répondu user164280 2009-08-27 15:10:05