Utilisation de variables dans un heredoc bash

201

J'essaye d'interpoler des variables à l'intérieur d'un heredoc bash:

var=$1
sudo tee "/path/to/outfile" > /dev/null << "EOF"
Some text that contains my $var
EOF

Cela ne fonctionne pas comme je m'y attendais ( $varest traité littéralement, pas développé).

J'ai besoin d'utiliser sudo teecar la création du fichier nécessite sudo. Faire quelque chose comme:

sudo cat > /path/to/outfile <<EOT
my text...
EOT

Ne fonctionne pas, car >outfileouvre le fichier dans le shell actuel, qui n'utilise pas sudo.

Jon
la source
9
C'est une confusion compréhensible! Comme indiqué ci-dessous, la citation d'une partie du délimiteur désactive l'expansion dans l'hérédoc (comme si elle était dans ''), mais ne pas la citation du délimiteur active l'expansion (comme si elle était dans ""). Cependant, votre intuition est correcte en Perl, où un heredoc avec un identifiant entre guillemets se comporte comme s'il était entre guillemets simples, un avec un identifiant entre guillemets doubles comme s'il était entre guillemets et un avec un identifiant rétrogradé comme s'il était entre guillemets ! Voir: perlop: << EOF
Nils von Barth

Réponses:

257

En réponse à votre première question, il n'y a pas de substitution de paramètre car vous avez mis le délimiteur entre guillemets - le manuel bash dit :

Le format des documents ici est:

      <<[-]word
              here-document
      delimiter

Aucune expansion de paramètre, substitution de commande, expansion arithmétique ou expansion de chemin n'est effectuée sur le mot . Si des caractères du mot sont entre guillemets, le délimiteur est le résultat de la suppression des guillemets sur mot et les lignes du document ici ne sont pas développées. Si le mot n'est pas entre guillemets, toutes les lignes du document ici sont soumises à l'expansion des paramètres, à la substitution de commandes et à l'expansion arithmétique. [...]

Si vous modifiez votre premier exemple pour utiliser à la <<EOFplace de, << "EOF"vous constaterez que cela fonctionne.

Dans votre deuxième exemple, le shell appelle sudouniquement avec le paramètre catet la redirection s'applique à la sortie de en sudo cattant qu'utilisateur d'origine. Cela fonctionnera si vous essayez:

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT
Mark Longair
la source
Si vous êtes intéressé, vous pouvez également le faire comme: (cat > /path/to/outfile) <<EOFà la place dusudo sh -c ... <<EOF
Voltaire
S'il vous plaît dites-moi que l'enterrement à Bash est une bonne raison pour laquelle il en est ainsi.
Landon Kuhn
101

N'utilisez pas de guillemets avec <<EOF:

var=$1
sudo tee "/path/to/outfile" > /dev/null <<EOF
Some text that contains my $var
EOF

L'expansion variable est le comportement par défaut dans here-docs. Vous désactivez ce comportement en citant l'étiquette (avec des guillemets simples ou doubles).

foule
la source
39

En corollaire tardif des réponses précédentes ici, vous vous retrouvez probablement dans des situations où vous voulez que certaines variables, mais pas toutes , soient interpolées. Vous pouvez résoudre ce problème en utilisant des contre-obliques pour échapper aux signes dollar et aux contre-obliques; ou vous pouvez mettre le texte statique dans une variable.

Name='Rich Ba$tard'
dough='$$$dollars$$$'
cat <<____HERE
$Name, you can win a lot of $dough this week!
Notice that \`backticks' need escaping if you want
literal text, not `pwd`, just like in variables like
\$HOME (current value: $HOME)
____HERE

Démo: https://ideone.com/rMF2XA

Notez que n'importe lequel des mécanismes de citation - \____HEREou "____HERE"ou '____HERE'- désactivera toute interpolation variable, et transformera le document ici en un morceau de texte littéral.

Une tâche courante consiste à combiner des variables locales avec un script qui doit être évalué par un shell, un langage de programmation ou un hôte distant différent.

local=$(uname)
ssh -t remote <<:
    echo "$local is the value from the host which ran the ssh command"
    # Prevent here doc from expanding locally; remote won't see backslash
    remote=\$(uname)
    # Same here
    echo "\$remote is the value from the host we ssh:ed to"
:
tripleee
la source
3
Je ne sais pas pourquoi cela a été voté à la baisse, mais cela ajoute une note valable qui n'est pas couverte dans la réponse désormais plus votée.
Inian