Avevo bisogno di scrivere uno script per inserire un input multi-linea in un programma (psql
).
Dopo un po 'di ricerca su google ho trovato la seguente sintassi:
cat << EOF | psql ---params
BEGIN;
`pg_dump ----something`
update table .... statement ...;
END;
EOF
Costruisce correttamente la stringa multilinea (da BEGIN;
a END;
, incluso) e la canalizza come input per psql
.
Ma non ho idea di come/perché funzioni, qualcuno può spiegare per favore?
Mi riferisco principalmente a cat << EOF
, conosco >
output su un file, >>
appende a un file, <
legge l'input dal file.
Che cosa fa esattamente <<
?
E c'è una pagina man per questo?
Questo è chiamato heredoc format per fornire una stringa in stdin. Vedi https://en.wikipedia.org/wiki/Here_document#Unix_shells per maggiori dettagli.
Da man bash
:
Qui documenti
Questo tipo di reindirizzamento indica alla Shell di leggere l'input da la sorgente corrente fino a una riga contiene solo Word (senza spazi vuoti finali ).
Tutte le righe lette fino a quel punto vengono quindi utilizzate come input standard per un comando.
Il formato di here-documents è:
<<[-]Word here-document delimiter
Nessuna espansione dei parametri, sostituzione dei comandi, espansione aritmetica o l'espansione del percorso viene eseguita su Parola. Se qualche carattere in Word è quotato, il delimitatore è il risultato della rimozione delle quote su Word e delle righe nel here-document non sono espansi . Se Word non è quotato, tutte le righe del here-document sono soggetti a espansione parametri, comando sostituzione e aritmetica espansione. In quest'ultimo caso, il sequenza di caratteri
\<newline>
is ignorato e\
deve essere usato per citare i caratteri\
,$
e`
.Se l'operatore di reindirizzamento è
<<-
, quindi tutti i caratteri di tabulazione iniziali vengono rimossi dalle linee di input e dal riga contenente delimitatore. Questo consente qui di inserire i documenti all'interno degli script di Shell in modo naturale.
La sintassi cat <<EOF
è molto utile quando si lavora con testo multilinea in Bash, ad es. quando si assegna una stringa multilinea a una variabile, un file o una pipe Shell.
cat <<EOF
in Bash:$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)
La variabile $sql
ora contiene anche i caratteri di nuova riga. Puoi verificare con echo -e "$sql"
.
$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF
Il file print.sh
ora contiene:
#!/bin/bash
echo $PWD
echo /home/user
$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF
Il file b.txt
contiene le righe bar
e baz
. Lo stesso output è stampato in stdout
.
Nel tuo caso, "EOF" è noto come "Here Tag". Fondamentalmente <<Here
dice alla Shell che si sta per inserire una stringa multilinea fino al "tag" Here
. Puoi nominare questo tag come vuoi, spesso EOF
o STOP
.
Alcune regole sui tag Here:
esempio:
$ 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
kennytm ha citato man bash
, ma la maggior parte è anche POSIX 7: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 :
Gli operatori di reindirizzamento "<<" e "<< -" consentono entrambi il reindirizzamento delle righe contenute in un file di input della shell, noto come "here-document", all'input di un comando.
Il presente documento deve essere trattato come una singola parola che inizia dopo quella successiva e continua fino a quando non c'è una riga contenente solo il delimitatore e una, senza caratteri nel mezzo. Quindi inizia il prossimo documento qui, se ce n'è uno. Il formato è il seguente:
[n]<<Word here-document delimiter
dove l'opzionale n rappresenta il numero del descrittore del file. Se il numero è omesso, il documento qui fa riferimento allo standard input (descrittore di file 0).
Se viene quotato qualsiasi carattere in Word, il delimitatore deve essere formato eseguendo la rimozione dell'offerta su Word e le righe del documento qui non devono essere espanse. Altrimenti, il delimitatore sarà la Parola stessa.
Se non vengono citati caratteri in Word, tutte le righe del documento qui devono essere espanse per l'espansione dei parametri, la sostituzione dei comandi e l'espansione aritmetica. In questo caso, l'input nell'input si comporta come le doppie virgolette interne (vedi Double-Quotes). Tuttavia, il carattere di doppia virgoletta ('"') non deve essere trattato in modo specifico all'interno di un documento qui, tranne quando la doppia virgoletta appare all'interno di" $ () "," `` "o" $ {} ".
Se il simbolo di reindirizzamento è "<< -", tutti i principali caratteri
<tab>
devono essere rimossi dalle linee di input e dalla riga contenente il delimitatore finale. Se più di un operatore "<<" o "<< -" è specificato su una linea, il presente documento associato al primo operatore deve essere fornito per primo dall'applicazione e deve essere letto per primo da Shell.Quando un documento qui viene letto da un dispositivo terminale e Shell è interattivo, deve scrivere il contenuto della variabile PS2, elaborato come descritto in Variabili Shell, in errore standard prima di leggere ogni riga di input fino a quando il delimitatore non è stato riconosciuto.
Alcuni esempi non ancora forniti.
Senza virgolette:
a=0
cat <<EOF
$a
EOF
Produzione:
0
Con citazioni:
a=0
cat <<'EOF'
$a
EOF
o (brutto ma valido):
a=0
cat <<E"O"F
$a
EOF
Uscite:
$a
Senza trattino:
cat <<EOF
<tab>a
EOF
dove <tab>
è una scheda letterale e può essere inserito con Ctrl + V <tab>
Produzione:
<tab>a
Con il trattino:
cat <<-EOF
<tab>a
<tab>EOF
Produzione:
a
Ciò ovviamente, in modo che tu possa indentare cat
come il codice circostante, che è più facile da leggere e conservare. Per esempio.:
if true; then
cat <<-EOF
a
EOF
fi
Sfortunatamente, questo non funziona per i caratteri spaziali: POSIX ha preferito il rientro tab
qui. Yikes.
Usando tee invece di cat
Non esattamente come una risposta alla domanda originale, ma ho voluto condividerla comunque: ho avuto la necessità di creare un file di configurazione in una directory che richiedesse i diritti di root.
Quanto segue non funziona per quel caso:
$ Sudo cat <<EOF >/etc/somedir/foo.conf
# my config file
foo=bar
EOF
perché il reindirizzamento viene gestito al di fuori del contesto Sudo.
Ho finito per usare questo invece:
$ Sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
Vale la pena notare che qui i documenti funzionano anche in bash loop. Questo esempio mostra come ottenere l'elenco di colonne della tabella:
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 ,
o anche senza la nuova linea
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
Questa non è necessariamente una risposta alla domanda originale, ma una condivisione di alcuni risultati dei miei test. Questo:
<<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test
produrrà lo stesso file di:
cat <<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test
Quindi, non vedo il punto di usare il comando cat.