Como escrever um heredoc em um arquivo no script Bash?


Respostas:


1073

Leia o Advanced Bash-Scripting Guide Capítulo 19. Aqui Documentos .

Aqui está um exemplo que gravará o conteúdo em um arquivo em /tmp/yourfilehere

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

Observe que o 'EOF' final (The LimitString) não deve ter nenhum espaço em branco na frente da palavra, porque isso significa que LimitStringele não será reconhecido.

Em um script de shell, convém usar o recuo para tornar o código legível, no entanto, isso pode ter o efeito indesejável de recuar o texto no documento aqui. Nesse caso, use <<-(seguido de um traço) para desativar as guias iniciais ( observe que para testar isso, você precisará substituir o espaço em branco inicial por um caractere de tabulação , pois não consigo imprimir caracteres de tabulação reais aqui.)

#!/usr/bin/env bash

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

Se você não quiser interpretar variáveis ​​no texto, use aspas simples:

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

Para canalizar o heredoc através de um pipeline de comando:

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

Resultado:

foo
bbr
bbz

... ou para escrever o heredoc em um arquivo usando sudo:

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

11
Você nem precisa do Bash, esse recurso também está presente nos shells Bourne / Korn / POSIX.
Janus Troelsen

5
e quanto <<<, como eles são chamados?
precisa

18
@PineappleUndertheSea <<<são chamados 'Here Strings'. Código como tr a-z A-Z <<< 'one two three'resultará na string ONE TWO THREE. Mais informações em en.wikipedia.org/wiki/Here_document#Here_strings
Stefan Lasiewski 10/09/13

8
O EOF final também não deve ter nenhum espaço em branco depois dele. Pelo menos no bash, isso resulta em sendo não reconhecido como delimitador
carpii

10
Uma vez que este heredoc particular, destina-se a ser conteúdo literal, em vez do que contêm substituições, ele deve ser <<'EOF', em vez de <<EOF.
Charles Duffy

152

Em vez de usar cato redirecionamento de E / S, pode ser útil usá-lo tee:

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

É mais conciso, além do operador de redirecionamento, ele pode ser combinado sudose você precisar gravar em arquivos com permissões de root.


18
Eu sugiro adicionar > /dev/nullno final da primeira linha para impedir que o conteúdo do arquivo aqui seja exibido em stdout quando ele é criado.
Joe Carroll

11
É verdade, mas a sua solução me atraiu por causa de sua compatibilidade com sudo, e não por causa de sua brevidade :-)
Joe Carroll

2
Como você usaria esse método para acrescentar a um arquivo existente?
MountainX

5
@MountainX Confira man tee. Use o -asinalizador para acrescentar em vez de substituir.
22413 Livven

3
Para uso em um script de configuração que às vezes preciso supervisionar, gosto mais deste porque imprime o conteúdo.
Alois Mahdal

59

Nota:

A pergunta (como escrever um documento aqui (aka heredoc ) em um arquivo em um script bash?) Tem (pelo menos) três principais dimensões ou subquestões independentes:

  1. Deseja sobrescrever um arquivo existente, anexá-lo a um arquivo existente ou gravar em um novo arquivo?
  2. Seu usuário ou outro usuário (por exemplo, root) possui o arquivo?
  3. Deseja escrever o conteúdo do seu heredoc literalmente ou fazer com que o bash interprete referências de variáveis ​​dentro do seu heredoc?

(Existem outras dimensões / subquestões que não considero importantes. Considere editar esta resposta para adicioná-las!) Aqui estão algumas das combinações mais importantes das dimensões da pergunta listada acima, com vários identificadores de delimitação diferentes - não há nada sagrado EOF, apenas certifique-se de que a string usada como seu identificador delimitante não ocorra dentro do seu heredoc:

  1. Para substituir um arquivo existente (ou gravar em um novo arquivo) que você possui, substituindo as referências de variáveis ​​dentro do heredoc:

    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. Para anexar um arquivo existente (ou gravar em um novo arquivo) que você possui, substituindo referências de variáveis ​​dentro do heredoc:

    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. Para substituir um arquivo existente (ou gravar em um novo arquivo) que você possui, com o conteúdo literal do heredoc:

    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. Para anexar um arquivo existente (ou gravar em um novo arquivo) que você possui, com o conteúdo literal do heredoc:

    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. Para substituir um arquivo existente (ou gravar em um novo arquivo) pertencente à raiz, substituindo as referências de variáveis ​​dentro do heredoc:

    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. Para acrescentar um arquivo existente (ou gravar em um novo arquivo) de propriedade de user = foo, com o conteúdo literal do heredoc:

    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

# 6 é o melhor. Mas como você substitui o conteúdo do arquivo existente pelo nº 6?
Aleksandr Makov

2
@Aleksandr Makov: como você substitui o conteúdo do arquivo existente com o # 6? Omita o -a== --append; ou seja, tee -a-> tee. Veja info tee(eu citá-lo aqui, mas comentário marcação é muito limitada.
TomRoche

1
Existe um benefício em # 6 usando gato e tubulação para tee em vez de sudo tee /path/to/your/file << 'Screw_you_Foo'?
precisa

Por que em FOEvez de EOFno exemplo anexado?
Becko

2
@ Becko: apenas para ilustrar que o rótulo é apenas um rótulo. Observe que eu usei um rótulo diferente em cada exemplo.
TomRoche

41

Para aproveitar a resposta de @ Livven , aqui estão algumas combinações úteis.

  1. substituição de variável, guia principal retido, sobrescrever arquivo, eco para stdout

    tee /path/to/file <<EOF
    ${variable}
    EOF
  2. nenhuma substituição de variável , guia principal retida, sobrescrever arquivo, eco para stdout

    tee /path/to/file <<'EOF'
    ${variable}
    EOF
  3. substituição de variável, guia inicial removido , sobrescrever arquivo, eco para stdout

    tee /path/to/file <<-EOF
        ${variable}
    EOF
  4. substituição de variável, guia principal retido, anexado ao arquivo , eco ao stdout

    tee -a /path/to/file <<EOF
    ${variable}
    EOF
  5. substituição de variável, guia principal retido, sobrescrever arquivo, sem eco para stdout

    tee /path/to/file <<EOF >/dev/null
    ${variable}
    EOF
  6. o acima pode ser combinado com sudotambém

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

16

Quando permissões de raiz são necessárias

Quando permissões de raiz são necessárias para o arquivo de destino, use em |sudo teevez de >:

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

É possível passar variáveis ​​para os documentos aqui? Como você conseguiu que o $ FOO fosse interpretado?
user1527227

@ user1527227 Como o exemplo do arquivo aqui diz: A variável $ FOO não será interpretada.
Serge Stroobandt

Abaixo , tentei combinar e organizar essa resposta com a de Stefan Lasiewski .
TomRoche 18/09

3
@ user1527227 Apenas não coloque o EOF entre aspas simples. Então $ FOO será interpretado.
Imnd_neel

11

Para futuras pessoas que possam ter esse problema, o seguinte formato funcionou:

(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
Não precisa dos parênteses: cat << END > afileseguido pelo heredoc funciona perfeitamente bem.
Glenn Jackman

Obrigado, isso realmente resolveu outro problema que encontrei. Depois de alguns documentos aqui, houve alguns problemas. Eu acho que tinha a ver com os parênteses, como nos conselhos acima.
Joshua Enfield

2
Isso não vai funcionar. O redirecionamento de saída precisa estar no final da linha que começa com, catcomo mostrado na resposta aceita.
Pausado até novo aviso.

2
@DennisWilliamson Funciona, é para isso que servem os parênteses. Toda a catroda dentro de um subnível, e toda a saída do subshell é redirecionado para o arquivo
Izkata

2
@ Izkata: Se você olhar para o histórico de edições desta resposta, os parênteses foram removidos antes de eu fazer o meu comentário e adicionados novamente. o comentário de glenn jackman (e o meu) se aplica.
Pausado até novo aviso.

3

Como exemplo, você pode usá-lo:

Primeiro (fazendo a conexão 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

Segundo (executando comandos):

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

Terceiro (executando comandos):

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

Quarto (usando variáveis):

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

Eu gosto deste método para concisão, legibilidade e apresentação em um script recuado:

<<-End_of_file >file
       foo bar
End_of_file

Onde →       está um tabpersonagem.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.