Wie funktioniert "cat << EOF" in Bash?

629

Ich musste ein Skript schreiben, um mehrzeilige Eingaben in ein Programm einzugeben ( psql).

Nach einigem googeln stellte ich fest, dass die folgende Syntax funktioniert:

cat << EOF | psql ---params
BEGIN;

`pg_dump ----something`

update table .... statement ...;

END;
EOF

Dadurch wird die mehrzeilige Zeichenfolge (von BEGIN;bis END;einschließlich) korrekt erstellt und als Eingabe an weitergeleitet psql.

Aber ich habe keine Ahnung, wie / warum es funktioniert, kann jemand bitte erklären?

Ich beziehe mich hauptsächlich auf cat << EOF, ich kenne >Ausgaben für eine Datei, >>hänge an eine Datei an, lese <Eingaben aus Dateien.

Was macht <<genau?

Und gibt es eine Manpage dafür?

hasen
quelle
26
Das ist wahrscheinlich eine nutzlose Verwendung von cat. Versuchen Sie psql ... << EOF ... Siehe auch "hier Zeichenfolgen". mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings
Bis auf weiteres angehalten.
1
Ich bin überrascht, dass es mit Katze funktioniert, aber nicht mit Echo. cat sollte einen Dateinamen als stdin erwarten, keine Zeichenfolge. psql << EOF klingt logisch, aber nicht anders. Funktioniert mit Katze, aber nicht mit Echo. Komisches Verhalten. Irgendein Hinweis darauf?
Alex
Ich antworte mir selbst: cat ohne Parameter wird ausgeführt und auf die Ausgabe repliziert, unabhängig davon, was über die Eingabe (stdin) gesendet wurde, und verwendet daher die Ausgabe, um die Datei über> zu füllen. Tatsächlich ist ein als Parameter gelesener Dateiname kein Standard-Stream.
Alex
@ Alex Echo druckt nur seine Befehlszeilenargumente aus, während es catstding liest (wenn es weitergeleitet wird) oder eine Datei liest, die seinen Befehlszeilenargumenten entspricht
The-null-Pointer -

Antworten:

517

Dies wird als Heredoc- Format bezeichnet , um eine Zeichenfolge in stdin bereitzustellen. Weitere Informationen finden Sie unter https://en.wikipedia.org/wiki/Here_document#Unix_shells .


Von man bash:

Hier Dokumente

Diese Art der Umleitung weist die Shell an, Eingaben von der aktuellen Quelle zu lesen, bis eine Zeile angezeigt wird, die nur ein Wort enthält (ohne nachgestellte Leerzeichen).

Alle bis zu diesem Punkt gelesenen Zeilen werden dann als Standardeingabe für einen Befehl verwendet.

Das Format der Here-Dokumente ist:

          <<[-]word
                  here-document
          delimiter

Für Word wird keine Parametererweiterung, Befehlssubstitution, arithmetische Erweiterung oder Pfadnamenerweiterung durchgeführt . Wenn Zeichen in Wörtern in Anführungszeichen gesetzt werden, ist das Trennzeichen das Ergebnis der Entfernung von Anführungszeichen in Word , und die Zeilen im Dokument hier werden nicht erweitert. Wenn das Wort nicht in Anführungszeichen gesetzt ist, werden alle Zeilen des hier beschriebenen Dokuments einer Parametererweiterung, einer Befehlssubstitution und einer arithmetischen Erweiterung unterzogen. Im letzteren Fall wird die Zeichenfolge \<newline>ignoriert und \muss zum Zitieren der Zeichen verwendet \werden.$ und` .

Wenn der Umleitungsoperator aktiviert ist <<-, werden alle führenden Tabulatorzeichen aus den Eingabezeilen und der Zeile mit dem Trennzeichen entfernt . Dadurch können hier Dokumente in Shell-Skripten auf natürliche Weise eingerückt werden.

kennytm
quelle
12
Es fiel mir am schwersten, die Variablen- / Parametererweiterung zu deaktivieren. Alles was ich tun musste war "doppelte Anführungszeichen" zu verwenden und das hat es behoben! Danke für die Information!
Xeoncross
11
Bezüglich <<-Bitte beachten Sie, dass nur führende Registerkarte Zeichen entfernt werden - nicht weich Tabulatoren. Dies ist einer der seltenen Fälle, in denen Sie das Tabulatorzeichen tatsächlich benötigen. Wenn der Rest Ihres Dokuments weiche Registerkarten verwendet, stellen Sie sicher, dass unsichtbare Zeichen angezeigt werden, und kopieren Sie (z. B.) ein Tabulatorzeichen und fügen Sie es ein. Wenn Sie es richtig machen, sollte Ihre Syntaxhervorhebung das Endtrennzeichen korrekt erfassen.
Trkoch
1
Ich sehe nicht, wie diese Antwort hilfreicher ist als die folgenden. Es
spuckt
@BrDaHa, vielleicht ist es nicht. Warum die Frage? wegen Upvotes? es war das einzige seit mehreren Jahren. Es wird durch Vergleichen von Daten gesehen.
Alexei Martianov
501

Das cat <<EOF Syntax ist sehr nützlich, wenn Sie mit mehrzeiligem Text in Bash arbeiten, z. Wenn Sie einer Shell-Variablen, einer Datei oder einer Pipe mehrzeilige Zeichenfolgen zuweisen.

Beispiele von cat <<EOF Verwendung Syntax in Bash:

1. Weisen Sie einer Shell-Variablen eine mehrzeilige Zeichenfolge zu

$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)

Die $sqlVariable enthält jetzt auch die Zeichen für neue Zeilen. Sie können mit überprüfenecho -e "$sql" .

2. Übergeben Sie eine mehrzeilige Zeichenfolge an eine Datei in Bash

$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF

Die print.shDatei enthält jetzt:

#!/bin/bash
echo $PWD
echo /home/user

3. Übergeben Sie eine mehrzeilige Zeichenfolge an ein Rohr in Bash

$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF

Die b.txtDatei enthält barund bazZeilen. Die gleiche Ausgabe wird gedruckt stdout.

Vojtech Vitek
quelle
1. 1 und 3 können ohne Katze durchgeführt werden; 2. Beispiel 1 kann mit einer einfachen mehrzeiligen Zeichenfolge durchgeführt werden
Daniel Alder
269

In Ihrem Fall wird "EOF" als "Here Tag" bezeichnet. Grundsätzlich <<Hereteilt die Shell mit, dass Sie eine mehrzeilige Zeichenfolge bis zum "Tag" eingeben werden Here. Sie können dieses Tag beliebig benennen, es ist häufig EOFoder STOP.

Einige Regeln zu den Here-Tags:

  1. Das Tag kann eine beliebige Zeichenfolge, ein Groß- oder Kleinbuchstabe sein, obwohl die meisten Benutzer konventionell Großbuchstaben verwenden.
  2. Das Tag wird nicht als Here-Tag betrachtet, wenn diese Zeile andere Wörter enthält. In diesem Fall wird es lediglich als Teil der Zeichenfolge betrachtet. Das Tag sollte sich in einer separaten Zeile befinden, um als Tag betrachtet zu werden.
  3. Das Tag sollte keine führenden oder nachfolgenden Leerzeichen in dieser Zeile enthalten, um als Tag betrachtet zu werden. Andernfalls wird es als Teil der Zeichenfolge betrachtet.

Beispiel:

$ cat >> test <<HERE
> Hello world HERE <-- Not by itself on a separate line -> not considered end of string
> This is a test
>  HERE <-- Leading space, so not considered end of string
> and a new line
> HERE <-- Now we have the end of the string
edelans
quelle
31
Dies ist die beste tatsächliche Antwort ... Sie definieren beide und geben klar den Hauptzweck der Verwendung anstelle der verwandten Theorie an ... was wichtig, aber nicht notwendig ist ... danke - super hilfreich
oemb1905
5
@edelans Sie müssen hinzufügen, dass bei <<-Verwendung der führenden Registerkarte nicht verhindert wird, dass das Tag erkannt wird
The-null-Pointer-
1
Ihre Antwort klickte mich auf "Sie werden eine mehrzeilige Zeichenfolge eingeben"
Kalkül
79

POSIX 7

kennytm zitiert man bash, aber das meiste davon ist auch POSIX 7: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 :

Die Umleitungsoperatoren "<<" und "<< -" ermöglichen beide die Umleitung von Zeilen, die in einer Shell-Eingabedatei, die als "Here-Dokument" bezeichnet wird, enthalten sind, zur Eingabe eines Befehls.

Das Dokument hier wird als ein einzelnes Wort behandelt, das nach dem nächsten beginnt und fortgesetzt wird, bis eine Zeile nur das Trennzeichen und a enthält, ohne dazwischen liegende Zeichen. Dann beginnt das nächste Here-Dokument, falls es eines gibt. Das Format ist wie folgt:

[n]<<word
    here-document
delimiter

Dabei steht das optionale n für die Dateideskriptornummer. Wenn die Nummer weggelassen wird, bezieht sich das Dokument hier auf die Standardeingabe (Dateideskriptor 0).

Wenn ein Zeichen in einem Wort in Anführungszeichen steht, wird das Trennzeichen durch Entfernen des Anführungszeichens für das Wort gebildet, und die Zeilen des Dokuments dürfen nicht erweitert werden. Andernfalls ist das Trennzeichen das Wort selbst.

Wenn keine Zeichen in Word angegeben sind, werden alle Zeilen des hier beschriebenen Dokuments zur Parametererweiterung, Befehlssubstitution und arithmetischen Erweiterung erweitert. In diesem Fall verhält sich das in der Eingabe wie die inneren doppelten Anführungszeichen (siehe doppelte Anführungszeichen). Das doppelte Anführungszeichen ('"') wird jedoch in einem Dokument hier nicht speziell behandelt, es sei denn, das doppelte Anführungszeichen erscheint in" $ () "," `` "oder" $ {} ".

Wenn das Umleitungssymbol "<< -" ist, werden alle führenden <tab>Zeichen von den Eingabezeilen und der Zeile mit dem nachfolgenden Trennzeichen entfernt. Wenn in einer Zeile mehr als ein "<<" - oder "<< -" - Operator angegeben ist, wird das dem ersten Operator zugeordnete Dokument zuerst von der Anwendung bereitgestellt und zuerst von der Shell gelesen.

Wenn ein Here-Dokument von einem Endgerät gelesen wird und die Shell interaktiv ist, muss es den Inhalt der Variablen PS2, die wie unter Shell-Variablen beschrieben verarbeitet wurde, auf Standardfehler schreiben, bevor jede Eingabezeile gelesen wird, bis das Trennzeichen erkannt wurde.

Beispiele

Einige Beispiele noch nicht angegeben.

Anführungszeichen verhindern die Parametererweiterung

Ohne Anführungszeichen:

a=0
cat <<EOF
$a
EOF

Ausgabe:

0

Mit Anführungszeichen:

a=0
cat <<'EOF'
$a
EOF

oder (hässlich aber gültig):

a=0
cat <<E"O"F
$a
EOF

Ausgänge:

$a

Bindestrich entfernt führende Tabulatoren

Ohne Bindestrich:

cat <<EOF
<tab>a
EOF

wo <tab>ist eine wörtliche Registerkarte und kann mit eingefügt werdenCtrl + V <tab>

Ausgabe:

<tab>a

Mit Bindestrich:

cat <<-EOF
<tab>a
<tab>EOF

Ausgabe:

a

Dies ist natürlich vorhanden, damit Sie catden umgebenden Code einrücken können , der einfacher zu lesen und zu warten ist. Z.B:

if true; then
    cat <<-EOF
    a
    EOF
fi

Leider funktioniert dies nicht für Leerzeichen: POSIX bevorzugte tabhier den Einzug. Huch.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
In Ihrem letzten Beispiel für <<-und <tab>asollte beachtet werden, dass der Zweck darin bestand, ein normales Einrücken von Code innerhalb des Skripts zu ermöglichen, während Heredoc-Text, der dem Empfangsprozess präsentiert wird, in Spalte 0 beginnen kann. Dies ist eine nicht allzu häufig vorkommende Funktion und ein bisschen Mehr Kontext kann viel Kopfkratzen verhindern ...
David C. Rankin
1
Wie kann ich mich der Erweiterung entziehen, wenn ein Teil des Inhalts zwischen meinen EOF-Tags erweitert werden muss und ein anderer nicht?
Jeanmichel Cote
2
... verwenden Sie einfach den Backslash vor der$
Jeanmichel Cote
@ JeanmichelCote Ich sehe keine bessere Option :-) Mit regulären Strings können Sie auch Anführungszeichen wie verwechseln "$a"'$b'"$c", aber hier gibt es kein Analogon AFAIK.
Ciro Santilli 法轮功 冠状 病 六四 事件 23
25

Verwenden Sie Tee anstelle von Katze

Nicht gerade als Antwort auf die ursprüngliche Frage, aber ich wollte dies trotzdem teilen: Ich musste eine Konfigurationsdatei in einem Verzeichnis erstellen, für das Root-Rechte erforderlich waren.

Folgendes funktioniert in diesem Fall nicht:

$ sudo cat <<EOF >/etc/somedir/foo.conf
# my config file
foo=bar
EOF

weil die Umleitung außerhalb des Sudo-Kontexts erfolgt.

Ich habe stattdessen folgendes verwendet:

$ sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
Andreas Maier
quelle
Verwenden Sie in Ihrem Fall sudo bash -c 'cat << EOF> /etc/somedir/foo.conf # meine Konfigurationsdatei foo = bar EOF'
wie
5

Eine kleine Erweiterung der obigen Antworten. Das Trailing >leitet die Eingabe in die Datei und überschreibt den vorhandenen Inhalt. Eine besonders bequeme Verwendung ist jedoch der Doppelpfeil >>, der angehängt wird und Ihren neuen Inhalt am Ende der Datei hinzufügt, wie in:

cat <<EOF >> /etc/fstab
data_server:/var/sharedServer/authority/cert /var/sharedFolder/sometin/authority/cert nfs
data_server:/var/sharedServer/cert   /var/sharedFolder/sometin/vsdc/cert nfs
EOF

Dies erweitert Ihre Möglichkeiten, fstabohne dass Sie sich Sorgen machen müssen, dass Sie versehentlich Inhalte ändern.

Lefty G Balogh
quelle
1

Dies ist nicht unbedingt eine Antwort auf die ursprüngliche Frage, sondern ein Austausch einiger Ergebnisse meiner eigenen Tests. Diese:

<<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

erzeugt die gleiche Datei wie:

cat <<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

Daher sehe ich keinen Sinn darin, den Befehl cat zu verwenden.


quelle
2
welche Schale? Ich habe mit Bash 4.4 unter Ubuntu 18.04 sowie Bash 3.2 unter OSX getestet. Beide haben eine leere Datei erstellt, wenn sie nur <<testohne verwendet wurden cat <<test.
wisbucky
Dies funktionierte für mich auf LInux Mint 19 Tara in zsh
Geoff Langenderfer
0

Erwähnenswert ist, dass hier Dokumente auch in Bash-Loops funktionieren. Dieses Beispiel zeigt, wie die Spaltenliste der Tabelle abgerufen wird:

export postgres_db_name='my_db'
export table_name='my_table_name'

# start copy 
while read -r c; do test -z "$c" || echo $table_name.$c , ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
SELECT column_name
FROM information_schema.columns
WHERE 1=1
AND table_schema = 'public'
AND table_name   =:'table_name'  ;
EOF
)
# stop copy , now paste straight into the bash shell ...

output: 
my_table_name.guid ,
my_table_name.id ,
my_table_name.level ,
my_table_name.seq ,

oder auch ohne die neue Zeile

while read -r c; do test -z "$c" || echo $table_name.$c , | perl -ne 
's/\n//gm;print' ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
 SELECT column_name
 FROM information_schema.columns
 WHERE 1=1
 AND table_schema = 'public'
 AND table_name   =:'table_name'  ;
 EOF
 )

 # output: daily_issues.guid ,daily_issues.id ,daily_issues.level ,daily_issues.seq ,daily_issues.prio ,daily_issues.weight ,daily_issues.status ,daily_issues.category ,daily_issues.name ,daily_issues.description ,daily_issues.type ,daily_issues.owner
Yordan Georgiev
quelle