Wie kann ich einen Heredoc in eine Datei im Bash-Skript schreiben?


Antworten:


1073

Lesen Sie das Advanced Bash-Scripting-Handbuch, Kapitel 19. Hier Dokumente .

Hier ist ein Beispiel, in dem der Inhalt in eine Datei unter geschrieben wird /tmp/yourfilehere

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

Beachten Sie, dass das letzte 'EOF' (The LimitString) kein Leerzeichen vor dem Wort haben sollte, da dies bedeutet, dass das LimitStringnicht erkannt wird.

In einem Shell-Skript möchten Sie möglicherweise Einrückungen verwenden, um den Code lesbar zu machen. Dies kann jedoch den unerwünschten Effekt haben, dass der Text in Ihrem Dokument hier eingerückt wird. Verwenden Sie in diesem Fall <<-(gefolgt von einem Bindestrich), um führende Tabulatoren zu deaktivieren. ( Beachten Sie, dass Sie zum Testen das führende Leerzeichen durch ein Tabulatorzeichen ersetzen müssen , da ich hier keine tatsächlichen Tabulatorzeichen drucken kann.)

#!/usr/bin/env bash

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

Wenn Sie keine Variablen im Text interpretieren möchten, verwenden Sie einfache Anführungszeichen:

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

So leiten Sie den Heredoc durch eine Befehlspipeline:

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

Ausgabe:

foo
bbr
bbz

... oder um den Heredoc in eine Datei zu schreiben mit sudo:

cat <<'EOF' |  sed 's/a/b/' | sudo tee /etc/config_file.conf
foo
bar
baz
EOF

11
Sie brauchen nicht einmal Bash, diese Funktion ist auch in den Bourne / Korn / POSIX-Shells enthalten.
Janus Troelsen

5
was ist mit <<<, wie heißen sie?
Jürgen Paul

18
@PineappleUndertheSea <<<heißen 'Here Strings'. Code like tr a-z A-Z <<< 'one two three'führt zur Zeichenfolge ONE TWO THREE. Weitere Informationen unter en.wikipedia.org/wiki/Here_document#Here_strings
Stefan Lasiewski

8
Der endgültige EOF sollte auch kein Leerzeichen danach haben. Zumindest bei Bash führt dies dazu, dass es nicht als Begrenzer
erkannt wird

10
Da dieser spezielle Heredoc eher wörtlichen Inhalt als Substitutionen enthalten soll, sollte er <<'EOF'eher als <<EOF.
Charles Duffy

152

Anstelle von catund E / A-Umleitung kann es nützlich sein, teestattdessen Folgendes zu verwenden :

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

Es ist prägnanter und kann im Gegensatz zum Umleitungsoperator kombiniert werden, sudowenn Sie in Dateien mit Root-Berechtigungen schreiben müssen.


18
Ich würde vorschlagen, > /dev/nullam Ende der ersten Zeile hinzuzufügen , um zu verhindern, dass der Inhalt der hier gezeigten Datei beim Erstellen stdout angezeigt wird.
Joe Carroll

11
Stimmt, aber Ihre Lösung hat mich eher wegen ihrer Kompatibilität mit sudoals wegen ihrer Kürze angesprochen :-)
Joe Carroll

2
Wie würden Sie diese Methode verwenden, um an eine vorhandene Datei anzuhängen?
MountainX

5
@MountainX Check out man tee. Verwenden Sie das -aFlag zum Anhängen anstatt zu überschreiben.
Livven

3
Für die Verwendung in einem Konfigurationsskript, das ich manchmal überwachen muss, gefällt mir dieses Skript mehr, da es den Inhalt druckt.
Alois Mahdal

59

Hinweis:

Die Frage (wie schreibt man hier ein Dokument (auch bekannt als Heredoc ) in eine Datei in einem Bash-Skript?) Hat (mindestens) drei unabhängige Hauptdimensionen oder Unterfragen:

  1. Möchten Sie eine vorhandene Datei überschreiben, an eine vorhandene Datei anhängen oder in eine neue Datei schreiben?
  2. Besitzt Ihr Benutzer oder ein anderer Benutzer (z. B. root) die Datei?
  3. Möchten Sie den Inhalt Ihres Heredocs buchstäblich schreiben oder Bash Variablenreferenzen in Ihrem Heredoc interpretieren lassen?

(Es gibt andere Dimensionen / Unterfragen, die ich nicht für wichtig halte. Bearbeiten Sie diese Antwort, um sie hinzuzufügen!) Hier sind einige der wichtigeren Kombinationen der Dimensionen der oben aufgeführten Frage mit verschiedenen abgrenzenden Bezeichnern - es gibt nichts Stellen Sie EOFeinfach sicher, dass die Zeichenfolge, die Sie als Begrenzungskennung verwenden, nicht in Ihrem Heredoc vorkommt:

  1. So überschreiben Sie eine vorhandene Datei (oder schreiben in eine neue Datei), die Sie besitzen, und ersetzen Sie Variablenreferenzen im heredoc:

    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. So hängen Sie eine vorhandene Datei an (oder schreiben in eine neue Datei), die Sie besitzen, und ersetzen Sie Variablenreferenzen im heredoc:

    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. So überschreiben Sie eine vorhandene Datei (oder schreiben in eine neue Datei), die Sie besitzen, mit dem wörtlichen Inhalt des Heredoc:

    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. So hängen Sie eine vorhandene Datei (oder schreiben Sie in eine neue Datei) an, die Sie besitzen, mit dem wörtlichen Inhalt des Heredocs:

    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. So überschreiben Sie eine vorhandene Datei (oder schreiben Sie in eine neue Datei), die root gehört, und ersetzen Sie Variablenreferenzen im heredoc:

    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. So hängen Sie eine vorhandene Datei (oder schreiben Sie in eine neue Datei) an, die user = foo gehört, mit dem wörtlichen Inhalt des 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
    

# 6 ist am besten. Aber wie überschreibt man den Inhalt einer vorhandenen Datei mit # 6?
Aleksandr Makov

2
@Aleksandr Makov: Wie überschreibt man den Inhalt einer vorhandenen Datei mit # 6? Lassen Sie das -a== weg --append; dh tee -a-> tee. Siehe info tee(ich würde es hier zitieren, aber das Kommentar-Markup ist zu begrenzt.
TomRoche

1
Gibt es einen Vorteil für # 6, wenn Katze und Paspel zum Abschlagen verwendet werden sudo tee /path/to/your/file << 'Screw_you_Foo'?
Codemonkee

Warum FOEstatt EOFim Anhang?
Becko

2
@becko: nur um zu veranschaulichen, dass das Etikett nur ein Etikett ist. Beachten Sie, dass ich in jedem Beispiel ein anderes Etikett verwendet habe.
TomRoche

41

Um auf der Antwort von @ Livven aufzubauen , finden Sie hier einige nützliche Kombinationen.

  1. Variablensubstitution, führende Registerkarte beibehalten, Datei überschreiben, Echo auf stdout

    tee /path/to/file <<EOF
    ${variable}
    EOF
    
  2. Keine Variablensubstitution , führende Registerkarte beibehalten, Datei überschreiben, Echo auf stdout

    tee /path/to/file <<'EOF'
    ${variable}
    EOF
    
  3. Variablensubstitution, führende Registerkarte entfernt , Datei überschreiben, Echo auf stdout

    tee /path/to/file <<-EOF
        ${variable}
    EOF
    
  4. Variablensubstitution, führende Registerkarte beibehalten, an Datei anhängen , Echo an stdout

    tee -a /path/to/file <<EOF
    ${variable}
    EOF
    
  5. Variablensubstitution, führende Registerkarte beibehalten, Datei überschreiben, kein Echo auf stdout

    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
    
  6. die oben kann kombiniert werden mit sudoals auch

    sudo -u USER tee /path/to/file <<EOF
    ${variable}
    EOF
    

16

Wenn Root-Berechtigungen erforderlich sind

Wenn Root-Berechtigungen für die Zieldatei erforderlich sind, verwenden Sie |sudo teeanstelle von >:

cat << 'EOF' |sudo tee /tmp/yourprotectedfilehere
The variable $FOO will *not* be interpreted.
EOF

Ist es möglich, Variablen an hier Dokumente zu übergeben? Wie konnte man es bekommen, damit $ FOO interpretiert wurde?
user1527227

@ user1527227 Wie im Beispiel hier die Datei sagt: Die Variable $ FOO wird nicht interpretiert.
Serge Stroobandt

Im Folgenden habe ich versucht, diese Antwort mit der von Stefan Lasiewski zu kombinieren und zu organisieren .
TomRoche

3
@ user1527227 Schließen Sie EOF nur nicht in einfache Anführungszeichen ein. Dann wird $ FOO interpretiert.
imnd_neel

11

Für zukünftige Personen, bei denen dieses Problem möglicherweise auftritt, hat das folgende Format funktioniert:

(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

6
Benötigen Sie keine Klammern: cat << END > afilegefolgt vom Heredoc funktioniert perfekt.
Glenn Jackman

Danke, dies hat tatsächlich ein anderes Problem gelöst, auf das ich gestoßen bin. Nach ein paar hier Dokumenten gab es einige Probleme. Ich denke, es hat mit den Eltern zu tun, wie es mit den obigen Ratschlägen behoben wurde.
Joshua Enfield

2
Das wird nicht funktionieren. Die Ausgabeumleitung muss am Ende der Zeile stehen, die catwie in der akzeptierten Antwort gezeigt beginnt .
Bis auf weiteres angehalten.

2
@ TennisWilliamson Es funktioniert, dafür sind die Eltern da. Das Ganze catläuft in einer Unterschale, und die gesamte Ausgabe der Unterschale wird in die Datei umgeleitet
Izkata

2
@Izkata: Wenn Sie sich den Bearbeitungsverlauf dieser Antwort ansehen, wurden die Klammern entfernt, bevor ich meinen Kommentar abgegeben und anschließend wieder hinzugefügt habe. Es gilt der Kommentar von Glenn Jackman (und mein).
Bis auf weiteres angehalten.

3

Als Beispiel könnten Sie es verwenden:

Zuerst (SSH-Verbindung herstellen):

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

Zweitens (Befehle ausführen):

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

Drittens (Befehle ausführen):

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

Viertens (unter Verwendung von Variablen):

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

-1

Ich mag diese Methode für Prägnanz, Lesbarkeit und Präsentation in einem eingerückten Skript:

<<-End_of_file >file
       foo bar
End_of_file

Wo →       ist ein tabCharakter?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.