219 votes

Utiliser des variables dans un heredoc bash

J'essaie 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 prévu ( $var est traité littéralement, et non élargi).

J'ai besoin d'utiliser sudo tee parce que la création du fichier nécessite sudo. En faisant quelque chose comme :

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

Ça ne marche pas, parce que >outfile ouvre le fichier dans le shell actuel, qui n'utilise pas sudo.

9 votes

C'est une confusion compréhensible ! Comme indiqué ci-dessous, le fait de citer n'importe quelle partie du délimiteur désactive l'expansion dans le hérédoc (comme s'il s'agissait de '' ), mais no La citation d'un sépar sépar séparateur active l'expansion (comme s'il s'agissait d'un sépar sépar séparateur dans le de l'expansion). "" ). Cependant, votre intuition est correcte en Perl, où un heredoc avec un identifiant entre guillemets simples se comporte comme s'il était entre guillemets simples, un avec un identifiant entre guillemets doubles comme s'il était entre guillemets doubles, et un avec un identifiant entre guillemets arrière comme s'il était entre guillemets arrière ! Voir : perlop : <<EOF

280voto

Mark Longair Points 93104

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

Le format des documents ici est le suivant :

      <<[-]word
              here-document
      delimiter

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

Si vous modifiez votre premier exemple pour utiliser <<EOF au lieu de << "EOF" vous verrez que ça marche.

Dans votre deuxième exemple, le shell invoque sudo uniquement avec le paramètre cat et la redirection s'applique à la sortie de sudo cat comme l'utilisateur original. Ça va marcher si vous essayez :

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT

0 votes

Si cela vous intéresse, vous pouvez aussi le faire en tant que.. : (cat > /path/to/outfile) <<EOF à la place de la sudo sh -c ... <<EOF

0 votes

Dis-moi que Bash a une bonne raison de le faire.

110voto

mob Points 61524

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 des variables est le comportement par défaut dans les here-docs. Vous pouvez désactiver ce comportement en citant l'étiquette (avec des guillemets simples ou doubles).

51voto

tripleee Points 28746

En guise de complément tardif aux réponses précédentes, vous vous retrouvez probablement dans des situations où vous voulez un peu de mais pas tous variables à interpoler. Vous pouvez résoudre ce problème en utilisant des barres obliques inversées pour échapper aux signes de dollar et aux barres obliques inversées, ou vous pouvez placer 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émonstration : https://ideone.com/rMF2XA

Notez que tous les mécanismes de citation -- \____HERE o "____HERE" o '____HERE' -- désactivera toute interpolation de variables, et transformera le document ici en un morceau de texte littéral.

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

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"
:

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X