Comment cat << EOF >> un fichier contenant du code?

101

Je souhaite imprimer du code dans un fichier en utilisant cat <<EOF >>:

cat <<EOF >> brightup.sh
!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

mais quand je vérifie la sortie du fichier, j'obtiens ceci:

!/bin/bash
curr=1634
if [  -lt 4477 ]; then
   curr=406;
   echo   > /sys/class/backlight/intel_backlight/brightness;
fi

J'ai essayé de mettre des guillemets simples, mais la sortie contient également les guillemets simples. Comment éviter ce problème?

UberNate
la source
2
Vous devriez également réparer le shebang. La première ligne doit être littéralement #!/bin/bashet rien d'autre - #!c'est ce qui en fait une ligne shebang valide, et ce qui vient après est le chemin vers l'interpréteur.
tripleee
1
voir man bashet rechercher Here Documents. Tous les détails ici.
Hong
1
En retard, la syntaxe moderne pour la substitution de processus est $(command)au lieu de `command`. Pour obtenir le contenu d'un fichier, Bash a$(<file)
tripleee

Réponses:

159

Vous n'avez besoin que d'un changement minime; guillemet simple le délimiteur ici-document après <<.

cat <<'EOF' >> brightup.sh

ou de manière équivalente contre la barre oblique inverse:

cat <<\EOF >>brightup.sh

Sans les guillemets, le document ici subira une substitution de variables, les backticks seront évalués, etc., comme vous l'avez découvert.

Si vous devez développer certaines valeurs, mais pas toutes, vous devez échapper individuellement à celles que vous souhaitez empêcher.

cat <<EOF >>brightup.sh
#!/bin/sh
# Created on $(date # : <<-- this will be evaluated before cat;)
echo "\$HOME will not be evaluated because it is backslash-escaped"
EOF

produira

#!/bin/sh
# Created on Fri Feb 16 11:00:18 UTC 2018
echo "$HOME will not be evaluated because it is backslash-escaped"

Comme suggéré par @fedorqui , voici la section pertinente de man bash:

Ici Documents

Ce type de redirection demande au shell de lire l'entrée de la source actuelle jusqu'à ce qu'une ligne contenant uniquement un délimiteur (sans espace de fin) soit vue. Toutes les lignes lues jusqu'à ce point sont ensuite utilisées comme entrée standard pour une commande.

Le format des documents ici est:

      <<[-]word
              here-document
      delimiter

Aucun développement de paramètre, substitution de commande, développement arithmétique ou développement de chemin n'est effectué 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 . Dans ce dernier cas, la séquence de caractères \ est ignorée et \ doit être utilisée pour citer les caractères \, $ et `.

tripleee
la source
1
Je vois que vous avez marqué Comment éviter les variables d'expansion heredoc? comme un double de celui-ci. Aucune objection, seulement que j'inclurais la référence des documents Bash, comme je l'ai fait dans le mien (qui n'ayant que 13k visites a obtenu près de 100 rep, donc cela semble très utile).
fedorqui 'SO stop nuire'
@fedorqui Vous souhaitez peut-être porter votre réponse à cette question? Ou nous pouvons changer le marquage en double pour être l'inverse; J'ai juste regardé le score de la question, pas la réponse marque beaucoup.
tripleee
Mmm qu'en est-il de les fusionner , d'avoir celui-ci comme question cible? La question en double a un bon titre, mais elle est trop longue. Cependant, cela a attiré beaucoup d'attention ces derniers temps (je reçois des votes positifs par mois ).
fedorqui 'SO arrêtez de nuire'
@fedorqui J'aime le concept de fusion mais je ne l'ai jamais vu se produire dans la pratique; si je comprends bien la situation, les mods ne sont tout simplement pas capables de gérer les fusions non triviales car les tracas et la complexité l'emportent de loin sur les avantages. Ce que j'ai fait une ou deux fois, c'est de supprimer une réponse utile et votée et de la republier sous une question différente, bien que je puisse difficilement recommander cette solution de contournement.
tripleee
Il est difficile de dire quand cela en vaut la peine. Je l'ai fait dans un site que je modifie quand une bonne question a été postée sans remarquer qu'il y en avait une autre bonne d'avant, les deux ayant de bonnes réponses. Supprimer ma réponse à plus de 90 points ne semble pas un bon plan, car cela laisserait cette question orpheline.
fedorqui 'SO arrêtez de nuire'
20

Ou, en utilisant vos marqueurs EOF, vous devez citer le marqueur initial pour que l'expansion ne soit pas effectuée:

#-----v---v------
cat <<'EOF' >> brightup.sh
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

IHTH

bombardier
la source
1
Je modifierais votre message, mais pour être sûr, ne devrait-il pas être #! / Bin / bash et pas! / Bin / bash?
Matthew Hoggan
@MatthewHoggan: Oui, vous avez raison! Merci d'avoir attrapé ça. Je le répare maintenant.
shellter du
16

Cela devrait fonctionner, je viens de le tester et cela a fonctionné comme prévu: aucune expansion, substitution ou autre chose n'a eu lieu.

cat <<< '
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
  curr=$((curr+406));
  echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi' > file # use overwrite mode so that you don't keep on appending the same script to that file over and over again, unless that's what you want. 

L'utilisation de ce qui suit fonctionne également.

cat <<< ' > file
 ... code ...'

En outre, il convient de noter que lors de l'utilisation d'heredocs, tels que la << EOFsubstitution et l'expansion de variables, etc. Alors faites quelque chose comme ça:

cat << EOF > file
cd "$HOME"
echo "$PWD" # echo the current path
EOF

entraînera toujours l'expansion des variables $HOMEet $PWD. Donc, si votre répertoire personnel est /home/foobaret le chemin actuel est /home/foobar/bin, fileressemblera à ceci:

cd "/home/foobar"
echo "/home/foobar/bin"

au lieu de l'attendu:

cd "$HOME"
echo "$PWD"
Alexej Magura
la source
2
Une chaîne ici entre guillemets simples ne peut évidemment pas contenir de guillemets simples, ce qui peut être un problème prohibitif. Ici, les documents sont la seule solution de contournement sensée si vous avez un script qui doit contenir à la fois des guillemets simples et doubles, bien que ce ne soit bien sûr pas le cas avec l'exemple simple de l'OP. De plus, tangentiellement, les chaînes ici <<<ne sont disponibles qu'à partir de Bash 3, et ne sont pas portables vers d'autres shells.
tripleee
<<<est aussi disponible dans Zsh
Alexej Magura
3
aujourd'hui, j'ai appris que vous pouvez faire en sorte que le nom de fichier suive immédiatement l'ouverture de l'hérédoc. Merci @AlexejMagura!
chaseadamsio