webentwicklung-frage-antwort-db.com.de

Wie funktioniert "cat << EOF" in bash?

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

Nach ein bisschen googeln fand ich folgende Syntaxwerke:

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 psql übergeben.

Aber ich habe keine Ahnung, wie und warum es funktioniert. Kann jemand bitte erklären?

Ich beziehe mich hauptsächlich auf cat << EOF, ich kenne > Ausgaben in eine Datei, >> hängt an eine Datei, < liest die Eingabe aus einer Datei. 

Was macht << genau?

Und gibt es dafür eine Manpage?

467
hasen

Dies wird heredoc format genannt, um einen String in stdin bereitzustellen. Siehe https://en.wikipedia.org/wiki/Here_document#Unix_shells für weitere Details.


Von man bash:

Hier Dokumente

Diese Art der Umleitung weist die Shell an, Eingaben aus .__ zu lesen. die aktuelle Quelle bis zu einer Zeile Es wird nur Word (ohne nachgestellte Leerzeichen) angezeigt.

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

Das Format der Here-Dokumente ist:

          <<[-]Word
                  here-document
          delimiter

Keine Parametererweiterung, Befehlssubstitution, arithmetische Erweiterung oder Die Erweiterung des Pfadnamens wird für .__ ausgeführt. Wort. Wenn Zeichen in Word .__ sind. zitiert die delimiter ist das Ergebnis der Entfernung von Anführungszeichen in Word und den Zeilen im hier-Dokument werden nicht erweitert . Wenn Word nicht in Anführungszeichen steht, werden alle Zeilen der here-document unterliegen der Parametererweiterung, Befehl Substitution und Arithmetik Erweiterung. Im letzteren Fall ist der Zeichenfolge \<newline> ist ignoriert und \ muss verwendet werden, um die Zeichen \, $ und ` zu zitieren.

Wenn der Umleitungsoperator <<- ist, werden alle führenden Tabulatorzeichen werden von den Eingabezeilen entfernt und die Zeile mit Trennzeichen. Diese Hiermit können Dokumente in Shell-Skripten auf natürliche Weise eingerückt werden.

417
kennytm

Die cat <<EOF-Syntax ist sehr nützlich, wenn Sie in Bash mit mehrzeiligem Text arbeiten, z. beim Zuweisen einer mehrzeiligen Zeichenfolge zu einer Shell-Variablen, einer Datei oder einer Pipe.

Beispiele für die Verwendung von cat <<EOF-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 Variable $sql enthält jetzt auch die Zeilenzeilen. Sie können dies mit echo -e "$sql" überprüfen.

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

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

Die print.sh-Datei enthält jetzt:

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

3. Übergeben Sie den mehrzeiligen String an eine Pipe in Bash

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

Die b.txt-Datei enthält bar- und baz-Zeilen. Dieselbe Ausgabe wird in stdout gedruckt.

380
Vojtech Vitek

In Ihrem Fall wird "EOF" als "Here-Tag" bezeichnet. Grundsätzlich sagt <<Here der Shell, dass Sie eine mehrzeilige Zeichenfolge bis zum "Tag" Here eingeben. Sie können dieses Tag nach Belieben benennen, oft EOF oder STOP.

Einige Regeln zu den Here-Tags:

  1. Das Tag kann eine beliebige Zeichenfolge, Groß- oder Kleinbuchstaben sein, obwohl die meisten Benutzer nach Konvention Großbuchstaben verwenden.
  2. Das Tag wird nicht als Here-Tag betrachtet, wenn andere Wörter in dieser Zeile enthalten sind. 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 nachgestellten Leerzeichen in dieser Zeile haben, um als Tag zu gelten. 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
179
edelans

POSIX 7

kennytm zitierte 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 ("Here-document") enthalten sind, zur Eingabe eines Befehls.

Das Here-Dokument wird als ein einzelnes Wort behandelt, das nach dem nächsten beginnt und fortgesetzt wird, bis es eine Zeile gibt, die nur das Trennzeichen und ein Zeichen enthält, zwischen denen keine Zeichen stehen. Dann startet das nächste Here-Dokument, falls vorhanden. 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 Here-Dokument auf die Standardeingabe (Dateideskriptor 0).

Wenn ein Zeichen in Word in Anführungszeichen gesetzt wird, muss das Trennzeichen durch Entfernen von Anführungszeichen in Word gebildet werden. Die Zeilen des Here-Dokuments dürfen nicht erweitert werden. Ansonsten ist das Trennzeichen das Wort selbst.

Wenn keine Zeichen in Word in Anführungszeichen stehen, müssen alle Zeilen des Here-Dokuments für die Parametererweiterung, die Befehlssubstitution und die Arithmetikerweiterung erweitert werden. In diesem Fall verhält sich das in der Eingabe als innere Anführungszeichen (siehe Anführungszeichen). Das Anführungszeichen ('' ') darf jedoch nicht speziell innerhalb eines Here-Dokuments behandelt werden, außer wenn das Anführungszeichen innerhalb von "$ ()", "` `" oder "$ {}" erscheint.

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

Wenn ein Here-Dokument von einem Endgerät gelesen wird und die Shell interaktiv ist, muss sie den Inhalt der Variablen PS2, die wie in Shell-Variablen beschrieben verarbeitet wird, in den Standardfehler schreiben, bevor sie jede Zeile der Eingabe liest, bis der Begrenzer erkannt wurde.

Beispiele

Einige Beispiele noch nicht gegeben.

Anführungszeichen verhindern die Parametererweiterung

Ohne Zitate:

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 Tabs

Ohne Bindestrich:

cat <<EOF
<tab>a
EOF

wobei <tab> eine literale Registerkarte ist und mit Ctrl + V <tab> eingefügt werden kann.

Ausgabe:

<tab>a

Mit Bindestrich:

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

Ausgabe:

a

Dies ist natürlich so, dass Sie Ihre cat wie den umgebenden Code einrücken können, der einfacher zu lesen und zu verwalten ist. Z.B.: 

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

Leider funktioniert das nicht für Leerzeichen: POSIX favorisierte hier den Einzug von tab. Yikes.

T-Stück anstelle von Katze verwenden

Nicht gerade als Antwort auf die ursprüngliche Frage, aber ich wollte es trotzdem weitergeben: Ich musste eine Konfigurationsdatei in einem Verzeichnis erstellen, das Root-Rechte benötigt.

Folgendes funktioniert für diesen Fall nicht:

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

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

Am Ende habe ich das verwendet:

$ Sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
19
Andreas Maier

Erwähnenswert ist, dass auch hier docs 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
0
Yordan Georgiev

Dies ist nicht unbedingt eine Antwort auf die ursprüngliche Frage, sondern das Teilen einiger Ergebnisse aus meinen 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

Ich sehe also keinen Sinn, den Befehl cat zu verwenden.

0
user9048395