Comment puis-je écrire un hérédoc dans un fichier en script Bash?

Réponses:

1074

Lisez le Guide de Bash-Scripting Avancé Chapitre 19. Ici Documents .

Voici un exemple qui va écrire le contenu dans un fichier à /tmp/yourfilehere

cat << EOF > /tmp/yourfilehere
These contents will be written to the file.
        This line is indented.
EOF

Notez que le 'EOF' final (Le LimitString) ne doit pas avoir d'espace devant le mot, car cela signifie que le LimitStringne sera pas reconnu.

Dans un script shell, vous souhaiterez peut-être utiliser l'indentation pour rendre le code lisible, mais cela peut avoir l'effet indésirable d'indenter le texte dans votre document ici. Dans ce cas, utilisez <<-(suivi d'un tiret) pour désactiver les tabulations de début ( Notez que pour tester cela, vous devrez remplacer le blanc de début par un caractère de tabulation , car je ne peux pas imprimer les vrais caractères de tabulation ici.)

#!/usr/bin/env bash

if true ; then
    cat <<- EOF > /tmp/yourfilehere
    The leading tab is ignored.
    EOF
fi

Si vous ne voulez pas interpréter les variables dans le texte, utilisez des guillemets simples:

cat << 'EOF' > /tmp/yourfilehere
The variable $FOO will not be interpreted.
EOF

Pour diriger l'hérédoc via un pipeline de commande:

cat <<'EOF' |  sed 's/a/b/'
foo
bar
baz
EOF

Production:

foo
bbr
bbz

... ou pour écrire l'hérédoc dans un fichier en utilisant sudo:

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF
Stefan Lasiewski
la source
11
Vous n'avez même pas besoin de Bash, cette fonctionnalité est également disponible dans les shells Bourne / Korn / POSIX.
Janus Troelsen
5
qu'en est-il <<<, comment s'appellent-ils?
Jürgen Paul
18
@PineappleUndertheSea <<<sont appelés «Here Strings». Un code comme tr a-z A-Z <<< 'one two three'entraînera la chaîne ONE TWO THREE. Plus d'informations sur en.wikipedia.org/wiki/Here_document#Here_strings
Stefan Lasiewski
8
La EOF finale ne devrait pas avoir aucun espace après qu'il soit. Au moins sur bash, il en résulte qu'il n'est pas reconnu comme le délimiteur
carpii
10
Étant donné que cet hérédoc particulier est destiné à être un contenu littéral, plutôt que de contenir des substitutions, il devrait l'être <<'EOF'plutôt que <<EOF.
Charles Duffy
154

Au lieu d'utiliser la catredirection d'E / S, il peut être utile d'utiliser à la teeplace:

tee newfile <<EOF
line 1
line 2
line 3
EOF

C'est plus concis, et contrairement à l'opérateur de redirection, il peut être combiné avec sudosi vous avez besoin d'écrire dans des fichiers avec des autorisations root.

Livven
la source
19
Je suggère d'ajouter > /dev/nullà la fin de la première ligne pour éviter que le contenu du fichier here ne s'affiche sur stdout lors de sa création.
Joe Carroll
11
Certes, mais votre solution m'a séduit en raison de sa compatibilité avec sudo, plutôt qu'en raison de sa brièveté :-)
Joe Carroll
2
Comment utiliseriez-vous cette méthode pour ajouter à un fichier existant?
MountainX
5
@MountainX Check out man tee. Utilisez l' -aindicateur pour ajouter au lieu d'écraser.
Livven
3
Pour une utilisation dans un script de configuration que j'ai parfois besoin de superviser, j'aime celui-ci de plus car il imprime le contenu.
Alois Mahdal
59

Remarque:

La question (comment écrire un document ici (aka heredoc ) dans un fichier dans un script bash?) A (au moins) 3 dimensions ou sous-questions indépendantes principales:

  1. Voulez-vous écraser un fichier existant, l'ajouter à un fichier existant ou écrire dans un nouveau fichier?
  2. Votre utilisateur ou un autre utilisateur (par exemple, root) est-il propriétaire du fichier?
  3. Voulez-vous écrire littéralement le contenu de votre hérédoc ou demander à bash d'interpréter les références de variables à l'intérieur de votre hérédoc?

(Il existe d'autres dimensions / sous-questions que je ne considère pas importantes. Envisagez de modifier cette réponse pour les ajouter!) Voici quelques-unes des combinaisons les plus importantes des dimensions de la question ci-dessus, avec différents identificateurs de délimitation différents - il n'y a rien sacré EOF, assurez-vous simplement que la chaîne que vous utilisez comme identifiant de délimitation ne se trouve pas dans votre hérédoc:

  1. Pour remplacer un fichier existant (ou écrire dans un nouveau fichier) que vous possédez, en remplaçant les références de variables à l'intérieur de l'hérédoc:

    cat << EOF > /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    EOF
  2. Pour ajouter un fichier existant (ou écrire dans un nouveau fichier) que vous possédez, en remplaçant les références de variables dans l'hérédoc:

    cat << FOE >> /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    FOE
  3. Pour remplacer un fichier existant (ou écrire dans un nouveau fichier) que vous possédez, avec le contenu littéral de l'hérédoc:

    cat << 'END_OF_FILE' > /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    END_OF_FILE
  4. Pour ajouter un fichier existant (ou écrire dans un nouveau fichier) que vous possédez, avec le contenu littéral de l'hérédoc:

    cat << 'eof' >> /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    eof
  5. Pour remplacer un fichier existant (ou écrire dans un nouveau fichier) appartenant à root, en remplaçant les références de variables à l'intérieur de l'hérédoc:

    cat << until_it_ends | sudo tee /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, with the variable contents substituted.
    until_it_ends
  6. Pour ajouter un fichier existant (ou écrire dans un nouveau fichier) appartenant à user = foo, avec le contenu littéral du heredoc:

    cat << 'Screw_you_Foo' | sudo -u foo tee -a /path/to/your/file
    This line will write to the file.
    ${THIS} will also write to the file, without the variable contents substituted.
    Screw_you_Foo
TomRoche
la source
# 6 est le meilleur. Mais comment écraser le contenu d'un fichier existant avec # 6?
Aleksandr Makov
2
@Aleksandr Makov: comment écraser le contenu d'un fichier existant avec # 6? Omettez le -a== --append; c'est-à-dire tee -a-> tee. Voir info tee(je le cite ici, mais le balisage des commentaires est trop limité.
TomRoche
1
Y a-t-il un avantage à utiliser le chat et la tuyauterie pour t-shirt au lieu de sudo tee /path/to/your/file << 'Screw_you_Foo'?
codemonkee
Pourquoi FOEau lieu de EOFdans l'exemple annexé?
Becko
2
@becko: juste pour illustrer que l'étiquette n'est qu'une étiquette. Notez que j'ai utilisé une étiquette différente dans chaque exemple.
TomRoche
41

Pour tirer parti de la réponse de @ Livven , voici quelques combinaisons utiles.

  1. substitution de variable, onglet de tête conservé, écraser le fichier, écho à stdout

    tee /path/to/file <<EOF
    ${variable}
    EOF
  2. pas de substitution de variable , onglet de tête conservé, écraser le fichier, écho à stdout

    tee /path/to/file <<'EOF'
    ${variable}
    EOF
  3. substitution de variable, onglet de tête supprimé , écraser le fichier, écho à stdout

    tee /path/to/file <<-EOF
        ${variable}
    EOF
  4. substitution de variable, onglet de tête conservé, ajout au fichier , écho à la sortie standard

    tee -a /path/to/file <<EOF
    ${variable}
    EOF
  5. substitution de variable, onglet de tête conservé, écraser le fichier, pas d'écho à la sortie standard

    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
  6. ci - dessus peuvent être combinés avec sudoaussi bien

    sudo -u USER tee /path/to/file <<EOF
    ${variable}
    EOF
go2null
la source
16

Lorsque les autorisations root sont requises

Lorsque les autorisations root sont requises pour le fichier de destination, utilisez à la |sudo teeplace de >:

cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
The variable $FOO will *not* be interpreted.
EOF
Serge Stroobandt
la source
Est-il possible de passer des variables à des documents ici? Comment pouvez-vous l'obtenir pour que $ FOO soit interprété?
user1527227
@ user1527227 Comme le montre l'exemple du fichier ici: La variable $ FOO ne sera pas interprétée.
Serge Stroobandt
Ci-dessous, j'ai essayé de combiner et d'organiser cette réponse avec celle de Stefan Lasiewski .
TomRoche
3
@ user1527227 Ne mettez pas EOF entre guillemets simples. Ensuite, $ FOO sera interprété.
imnd_neel
11

Pour les personnes futures qui pourraient avoir ce problème, le format suivant a fonctionné:

(cat <<- _EOF_
        LogFile /var/log/clamd.log
        LogTime yes
        DatabaseDirectory /var/lib/clamav
        LocalSocket /tmp/clamd.socket
        TCPAddr 127.0.0.1
        SelfCheck 1020
        ScanPDF yes
        _EOF_
) > /etc/clamd.conf
Joshua Enfield
la source
6
Pas besoin de parenthèses: cat << END > afilesuivi de l'hérédoc, il fonctionne parfaitement bien.
glenn jackman
Merci, cela a en fait résolu un autre problème que j'ai rencontré. Après quelques documents ici, il y avait quelques problèmes. Je pense que cela avait à voir avec les parens, comme avec les conseils ci-dessus, il l'a fixé.
Joshua Enfield
2
Ça ne marchera pas. La redirection de sortie doit être à la fin de la ligne qui commence catcomme indiqué dans la réponse acceptée.
pause jusqu'à nouvel ordre.
2
@DennisWilliamson Ça marche, c'est à ça que servent les parens. L'ensemble cats'exécute à l'intérieur d'un sous-shell, et toute la sortie du sous-shell est redirigée vers le fichier
Izkata
2
@Izkata: Si vous regardez l'historique des modifications de cette réponse, les parenthèses ont été supprimées avant de faire mon commentaire et ajoutées après. le commentaire de glenn jackman (et mon) s'applique.
pause jusqu'à nouvel ordre.
3

Par exemple, vous pouvez l'utiliser:

Premièrement (connexion ssh):

while read pass port user ip files directs; do
    sshpass -p$pass scp -o 'StrictHostKeyChecking no' -P $port $files $user@$ip:$directs
done <<____HERE
    PASS    PORT    USER    IP    FILES    DIRECTS
      .      .       .       .      .         .
      .      .       .       .      .         .
      .      .       .       .      .         .
    PASS    PORT    USER    IP    FILES    DIRECTS
____HERE

Deuxième (exécution des commandes):

while read pass port user ip; do
    sshpass -p$pass ssh -p $port $user@$ip <<ENDSSH1
    COMMAND 1
    .
    .
    .
    COMMAND n
ENDSSH1
done <<____HERE
    PASS    PORT    USER    IP
      .      .       .       .
      .      .       .       .
      .      .       .       .
    PASS    PORT    USER    IP    
____HERE

Troisième (exécution des commandes):

Script=$'
#Your commands
'

while read pass port user ip; do
    sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip "$Script"

done <<___HERE
PASS    PORT    USER    IP
  .      .       .       .
  .      .       .       .
  .      .       .       .
PASS    PORT    USER    IP  
___HERE

Quatrième (en utilisant des variables):

while read pass port user ip fileoutput; do
    sshpass -p$pass ssh -o 'StrictHostKeyChecking no' -p $port $user@$ip fileinput=$fileinput 'bash -s'<<ENDSSH1
    #Your command > $fileinput
    #Your command > $fileinput
ENDSSH1
done <<____HERE
    PASS    PORT    USER    IP      FILE-OUTPUT
      .      .       .       .          .
      .      .       .       .          .
      .      .       .       .          .
    PASS    PORT    USER    IP      FILE-OUTPUT
____HERE
MLSC
la source
-1

J'aime cette méthode pour la concision, la lisibilité et la présentation dans un script en retrait:

<<-End_of_file >file
       foo bar
End_of_file

→       est un tabpersonnage.

dan
la source