it-swarm-eu.dev

Come funziona "cat << EOF" in bash?

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?

467
hasen

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.

417
kennytm

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.

Esempi di utilizzo della sintassi cat <<EOF in Bash:

1. Assegnare una stringa multi-linea a una variabile Shell

$ 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".

2. Passare la stringa multi-linea in un file in Bash

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

Il file print.sh ora contiene:

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

3. Passare la stringa multi-linea a una pipe in Bash

$ 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.

380
Vojtech Vitek

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:

  1. Il tag può essere qualsiasi stringa, maiuscolo o minuscolo, anche se la maggior parte delle persone usa maiuscole per convenzione.
  2. Il tag non sarà considerato come tag Here se ci sono altre parole in quella linea. In questo caso, sarà semplicemente considerato parte della stringa. Il tag dovrebbe essere di per sé su una riga separata, per essere considerato un tag.
  3. Il tag non dovrebbe avere spazi iniziali o finali in quella linea per essere considerato un tag. Altrimenti sarà considerato come parte della stringa.

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
179
edelans

POSIX 7

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.

Esempi

Alcuni esempi non ancora forniti.

Le quotazioni impediscono l'espansione dei parametri

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

Hyphen rimuove le schede principali

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
19
Andreas Maier

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
0
Yordan Georgiev

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.

0
user9048395