Usando variáveis ​​dentro de um heredoc bash

192

Estou tentando interpolar variáveis ​​dentro de um heredoc bash:

var=$1
sudo tee "/path/to/outfile" > /dev/null << "EOF"
Some text that contains my $var
EOF

Isso não está funcionando como eu esperaria ( $varé tratado literalmente, não expandido).

Eu preciso usar sudo teeporque a criação do arquivo requer sudo. Fazendo algo como:

sudo cat > /path/to/outfile <<EOT
my text...
EOT

Não funciona, porque >outfileabre o arquivo no shell atual, que não está usando o sudo.

Jon
fonte
9
Esta é uma confusão compreensível! Como observado abaixo, citar qualquer parte do delimitador desativa a expansão no heredoc (como se estivesse dentro ''), mas não citar o delimitador ativa a expansão (como se estivesse dentro ""). No entanto, sua intuição está correta no Perl, onde um heredoc com identificador de aspas simples se comporta como se estivesse entre aspas simples, um com um identificador de aspas duplas como se estivesse entre aspas duplas e outro com identificador de retorno marcado como se estivesse em backticks ! Veja: perlop: << EOF
Nils von Barth

Respostas:

252

Em resposta à sua primeira pergunta, não há substituição de parâmetro porque você colocou o delimitador entre aspas - o manual do bash diz :

O formato dos documentos aqui é:

      <<[-]word
              here-document
      delimiter

Nenhuma expansão de parâmetro, substituição de comando, expansão aritmética ou expansão de nome de caminho é executada no word . Se algum caractere no word for citado, o delimitador é o resultado da remoção de aspas no word e as linhas no documento aqui não são expandidas. Se a palavra não estiver entre aspas, todas as linhas do documento aqui estão sujeitas a expansão de parâmetro, substituição de comando e expansão aritmética. [...]

Se você alterar seu primeiro exemplo para usar em <<EOFvez de, << "EOF"verá que funciona.

No seu segundo exemplo, o shell chama sudoapenas com o parâmetro cate o redirecionamento se aplica à saída sudo catcomo o usuário original. Funcionará se você tentar:

sudo sh -c "cat > /path/to/outfile" <<EOT
my text...
EOT
Mark Longair
fonte
Se você estiver interessado, você também pode fazer isso como: (cat > /path/to/outfile) <<EOFno lugar desudo sh -c ... <<EOF
Voltaire
Por favor, diga-me que enterrado no Bash é uma boa razão para isso.
Landon Kuhn
96

Não use aspas com <<EOF:

var=$1
sudo tee "/path/to/outfile" > /dev/null <<EOF
Some text that contains my $var
EOF

Expansão variável é o comportamento padrão nos documentos aqui. Você desativa esse comportamento citando o rótulo (com aspas simples ou duplas).

multidão
fonte
36

Como corolário tardio das respostas anteriores aqui, você provavelmente acaba em situações em que deseja que algumas, mas nem todas as variáveis ​​sejam interpoladas. Você pode resolver isso usando barras invertidas para escapar de cifrões e reticulares; ou você pode colocar o texto estático em uma variável.

Name='Rich Ba$tard'
dough='$$$dollars$$$'
cat <<____HERE
$Name, you can win a lot of $dough this week!
Notice that \`backticks' need escaping if you want
literal text, not `pwd`, just like in variables like
\$HOME (current value: $HOME)
____HERE

Demonstração: https://ideone.com/rMF2XA

Observe que qualquer um dos mecanismos de citação - \____HEREou "____HERE"ou '____HERE'- desabilitará toda a interpolação de variáveis ​​e transformará o documento aqui em um pedaço de texto literal.

Uma tarefa comum é combinar variáveis ​​locais com scripts, que devem ser avaliados por um shell, linguagem de programação ou host remoto diferente.

local=$(uname)
ssh -t remote <<:
    echo "$local is the value from the host which ran the ssh command"
    # Prevent here doc from expanding locally; remote won't see backslash
    remote=\$(uname)
    # Same here
    echo "\$remote is the value from the host we ssh:ed to"
:
triplo
fonte
3
Não tenho certeza por que isso foi recusado, mas adiciona uma nota válida que não é abordada na resposta agora mais votada.
Inian