Come posso scrivere un heredoc in un file nello script Bash?


Risposte:


1073

Leggi la Guida avanzata agli script di Bash, capitolo 19. Qui, documenti .

Ecco un esempio che scriverà il contenuto in un file su /tmp/yourfilehere

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

Si noti che l'ultimo 'EOF' (The LimitString) non dovrebbe avere spazi bianchi davanti alla parola, perché significa che ilLimitString non verrà riconosciuto.

In uno script di shell, potresti voler usare il rientro per rendere leggibile il codice, tuttavia ciò può avere l'effetto indesiderato di indentare il testo all'interno del tuo documento qui. In questo caso, utilizzare <<-(seguito da un trattino) per disabilitare le schede iniziali (si noti che per verificare ciò è necessario sostituire lo spazio bianco iniziale con un carattere di tabulazione , poiché qui non è possibile stampare caratteri di tabulazione effettivi).

#!/usr/bin/env bash

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

Se non vuoi interpretare le variabili nel testo, usa le virgolette singole:

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

Per convogliare l'ereditarietà attraverso una pipeline di comandi:

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

Produzione:

foo
bbr
bbz

... o per scrivere l'ereditarietà in un file usando sudo:

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

11
Non hai nemmeno bisogno di Bash, questa funzione è presente anche nelle shell Bourne / Korn / POSIX.
Janus Troelsen,

5
che dire <<<, come si chiamano?
Jürgen Paul,

18
@PineappleUndertheSea <<<sono chiamati 'Here Strings'. Codice come tr a-z A-Z <<< 'one two three'si tradurrà nella stringa ONE TWO THREE. Maggiori informazioni su en.wikipedia.org/wiki/Here_document#Here_strings
Stefan Lasiewski

8
Anche l'EOF finale non dovrebbe avere spazi bianchi dopo di esso. Almeno su bash, questo si traduce nel fatto che non viene riconosciuto come delimitatore
carpii,

10
Poiché questa eredità particolare è intesa come contenuto letterale, piuttosto che contenere sostituzioni, dovrebbe essere <<'EOF'piuttosto che <<EOF.
Charles Duffy,

153

Invece di utilizzare cate il reindirizzamento I / O potrebbe essere utile utilizzare teeinvece:

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

È più conciso, inoltre a differenza dell'operatore di reindirizzamento con cui può essere combinato sudose è necessario scrivere su file con permessi di root.


19
Suggerirei di aggiungere > /dev/nullalla fine della prima riga per evitare che il contenuto del file qui venga visualizzato su stdout quando viene creato.
Joe Carroll,

11
È vero, ma la tua soluzione mi è piaciuta per la sua compatibilità sudo, piuttosto che per la sua brevità :-)
Joe Carroll,

2
Come useresti questo metodo per aggiungere a un file esistente?
MountainX,

5
@MountainX Scopri man tee. Utilizzare il -aflag per aggiungere anziché sovrascrivere.
Livven,

3
Per l'uso in uno script di configurazione che a volte ho bisogno di supervisionare, questo mi piace di più perché stampa il contenuto.
Alois Mahdal,

59

Nota:

La domanda (come scrivere un documento qui (aka heredoc ) in un file in uno script bash?) Ha (almeno) 3 principali dimensioni o domande secondarie indipendenti:

  1. Vuoi sovrascrivere un file esistente, aggiungerlo a un file esistente o scrivere in un nuovo file?
  2. Il tuo utente o un altro utente (ad es. root ) possiede il file?
  3. Vuoi scrivere letteralmente il contenuto della tua eredità o far interpretare bash i riferimenti alle variabili all'interno della tua eredità?

(Ci sono altre dimensioni / domande secondarie che non considero importanti. Prendi in considerazione la modifica di questa risposta per aggiungerle!) Ecco alcune delle combinazioni più importanti delle dimensioni della domanda sopra elencate, con vari identificatori di delimitazione diversi - non c'è nulla sacro EOF, assicurati solo che la stringa che usi come identificatore di delimitazione non si verifichi all'interno della tua eredità:

  1. Per sovrascrivere un file esistente (o scrivere in un nuovo file) di tua proprietà, sostituendo i riferimenti alle variabili all'interno dell'ereditarietà:

    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. Per aggiungere un file esistente (o scrivere in un nuovo file) di tua proprietà, sostituendo i riferimenti alle variabili all'interno dell'ereditarietà:

    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. Per sovrascrivere un file esistente (o scrivere in un nuovo file) di tua proprietà, con i contenuti letterali dell'ereditarietà:

    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. Per aggiungere un file esistente (o scrivere in un nuovo file) di tua proprietà, con i contenuti letterali dell'ereditarietà:

    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. Per sovrascrivere un file esistente (o scrivere in un nuovo file) di proprietà di root, sostituendo i riferimenti alle variabili all'interno dell'ereditarietà:

    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. Per aggiungere un file esistente (o scrivere in un nuovo file) di proprietà di user = foo, con i contenuti letterali dell'ereditarietà:

    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

Il numero 6 è il migliore. Ma come si sovrascrive il contenuto del file esistente con # 6?
Aleksandr Makov,

2
@Aleksandr Makov: come si sovrascrive il contenuto del file esistente con # 6? Ometti il -a== --append; cioè, tee -a-> tee. Vedi info tee(lo
citerei

1
C'è un vantaggio nell'uso # 6 di cat e piping per fare il tee invece di sudo tee /path/to/your/file << 'Screw_you_Foo'?
codemonkee

Perché FOEanziché EOFnell'esempio di append?
becko,

2
@becko: solo per illustrare che l'etichetta è solo un'etichetta. Nota che ho usato un'etichetta diversa in ogni esempio.
TomRoche,

41

Per costruire sulla risposta di @ Livven , ecco alcune utili combinazioni.

  1. sostituzione variabile, scheda iniziale mantenuta, sovrascrivi file, eco a stdout

    tee /path/to/file <<EOF
    ${variable}
    EOF
  2. nessuna sostituzione variabile , scheda iniziale mantenuta, sovrascrivi file, eco a stdout

    tee /path/to/file <<'EOF'
    ${variable}
    EOF
  3. sostituzione variabile, tabulazione rimossa , sovrascrivi file, eco a stdout

    tee /path/to/file <<-EOF
        ${variable}
    EOF
  4. sostituzione variabile, scheda iniziale mantenuta, aggiunta a file , eco a stdout

    tee -a /path/to/file <<EOF
    ${variable}
    EOF
  5. sostituzione variabile, scheda iniziale mantenuta, sovrascrivi file, nessuna eco allo stdout

    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
  6. quanto sopra può essere combinato con sudopure

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

16

Quando sono necessarie le autorizzazioni di root

Quando sono necessarie le autorizzazioni di root per il file di destinazione, utilizzare |sudo teeinvece di >:

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

È possibile passare variabili ai documenti qui? Come hai potuto ottenerlo, così $ FOO è stato interpretato?
user1527227,

@ user1527227 Come dice il file di esempio qui: la variabile $ FOO non verrà interpretata.
Serge Stroobandt,

Di seguito ho tentato di combinare e organizzare questa risposta con quella di Stefan Lasiewski .
TomRoche,

3
@ user1527227 Basta non racchiudere EOF tra virgolette singole. Quindi verrà interpretato $ FOO.
imnd_neel,

11

Per le persone future che potrebbero avere questo problema ha funzionato il seguente formato:

(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
Non ho bisogno delle parentesi: cat << END > afileseguite dall'eredità funziona perfettamente.
Glenn Jackman,

Grazie, questo in realtà ha risolto un altro problema che ho riscontrato. Dopo alcuni documenti qui c'erano alcuni problemi. Penso che abbia avuto a che fare con i genitori, come con il consiglio sopra risolto.
Joshua Enfield,

2
Questo non funzionerà. Il reindirizzamento dell'output deve essere alla fine della riga che inizia con, catcome mostrato nella risposta accettata.
In pausa fino a nuovo avviso.

2
@DennisWilliamson Funziona, ecco a cosa servono i genitori. L'intero catviene eseguito all'interno di una subshell e tutto l'output della subshell viene reindirizzato al file
Izkata

2
@Izkata: se guardi la cronologia delle modifiche di questa risposta, le parentesi sono state rimosse prima che io facessi il mio commento e poi le aggiungessi di nuovo. Si applica il commento (e il mio) di glenn jackman.
In pausa fino a nuovo avviso.

3

Ad esempio puoi usarlo:

Primo (fare una connessione ssh):

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

Secondo (esecuzione comandi):

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

Terzo (esecuzione dei comandi):

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

Avanti (usando le variabili):

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

Mi piace questo metodo per la concisione, la leggibilità e la presentazione in uno script rientrato:

<<-End_of_file >file
       foo bar
End_of_file

Dov'è →       un tabpersonaggio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.