Como cat << EOF >> um arquivo contendo código?

101

Quero imprimir o código em um arquivo usando cat <<EOF >>:

cat <<EOF >> brightup.sh
!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

mas quando eu verifico a saída do arquivo, recebo o seguinte:

!/bin/bash
curr=1634
if [  -lt 4477 ]; then
   curr=406;
   echo   > /sys/class/backlight/intel_backlight/brightness;
fi

Tentei colocar aspas simples, mas a saída também traz as aspas simples. Como posso evitar esse problema?

UberNate
fonte
2
Você também deve consertar o shebang. A primeira linha precisa ser literalmente #!/bin/bashe nada mais - #!isso é o que a torna uma linha shebang válida, e o que vem depois dela é o caminho para o intérprete.
tripleee
1
veja man bashe pesquise Here Documents. Todos os detalhes aí.
Hong,
1
Deixando de lado, a sintaxe moderna para substituição de processos é, em $(command)vez de `command`. Para obter o conteúdo de um arquivo, o Bash tem$(<file)
tripleee

Respostas:

159

Você só precisa de uma mudança mínima; apóie o delimitador here-document após <<.

cat <<'EOF' >> brightup.sh

ou de forma equivalente barra invertida com escape:

cat <<\EOF >>brightup.sh

Sem citar, o aqui documento sofrerá substituição de variáveis, crases serão avaliados, etc, como você descobriu.

Se você precisar expandir alguns, mas não todos os valores, será necessário escapar individualmente daqueles que deseja evitar.

cat <<EOF >>brightup.sh
#!/bin/sh
# Created on $(date # : <<-- this will be evaluated before cat;)
echo "\$HOME will not be evaluated because it is backslash-escaped"
EOF

vai produzir

#!/bin/sh
# Created on Fri Feb 16 11:00:18 UTC 2018
echo "$HOME will not be evaluated because it is backslash-escaped"

Conforme sugerido por @fedorqui , aqui está a seção relevante de man bash:

Aqui Documentos

Este tipo de redirecionamento instrui o shell a ler a entrada da fonte atual até que uma linha contendo apenas o delimitador (sem espaços em branco à direita) seja vista. Todas as linhas lidas até esse ponto são então usadas como entrada padrão para um comando.

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 na palavra. Se algum caractere da palavra estiver entre aspas, o delimitador é o resultado da remoção da citação na palavra, e as linhas do here-document não são expandidas. Se a palavra não estiver entre aspas, todas as linhas do here-document estão sujeitas a expansão de parâmetro, substituição de comando e expansão aritmética . No último caso, a seqüência de caracteres \ é ignorada e \ deve ser usada para citar os caracteres \, $ e `.

triplo
fonte
1
Vejo que você marcou Como evitar variáveis ​​de expansão heredoc? como uma duplicata deste. Sem objeções, apenas que eu incluiria a referência aos documentos do Bash, como fiz no meu (que tendo apenas 13 mil visitas obteve quase 100 repetições, então parece ser bastante útil).
fedorqui 'SO pare de prejudicar'
@fedorqui Talvez você queira realmente portar sua resposta a esta pergunta? Ou podemos mudar a marcação duplicada para o contrário; Eu apenas olhei para a pontuação da pergunta, não a pontuação das respostas.
tripleee
Mmm, que tal fundi- los, tendo este como a questão alvo? A pergunta duplicada tem um bom título, só que é muito longo. No entanto, de alguma forma, ele chamou muita atenção recentemente (estou recebendo alguns votos positivos por mês ).
fedorqui 'ASSIM, pare de prejudicar'
@fedorqui Gosto do conceito de fusão, mas nunca vi isso acontecer na prática; se entendi a situação corretamente, os mods simplesmente não são capazes de lidar com mesclagens não triviais porque os aborrecimentos e complexidades superam em muito os benefícios. O que fiz uma ou duas vezes foi excluir uma resposta útil e votada novamente e publicá-la novamente em uma pergunta diferente, embora dificilmente possa recomendar essa solução alternativa.
tripleee
É difícil dizer quando vale a pena fazer. Já fiz isso em um site que modifiquei quando uma boa pergunta foi postada sem perceber que havia outra boa de antes, ambas com boas respostas. Remover minha resposta com pontuação de 90+ não parece um bom plano, pois deixaria essa pergunta órfã.
fedorqui 'ASSIM, pare de prejudicar'
20

Ou, usando seus marcadores EOF, você precisa citar o marcador inicial para que a expansão não seja feita:

#-----v---v------
cat <<'EOF' >> brightup.sh
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
   curr=$((curr+406));
   echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi
EOF

IHTH

shellter
fonte
1
Gostaria de editar sua postagem, mas só por segurança não deveria ser #! / Bin / bash e não! / Bin / bash?
Matthew Hoggan
@MatthewHoggan: Sim, você está certo! Obrigado por pegar isso. Estou consertando agora.
shellter
16

Isso deve funcionar, eu apenas testei e funcionou como esperado: nenhuma expansão, substituição ou o que quer que tenha ocorrido.

cat <<< '
#!/bin/bash
curr=`cat /sys/class/backlight/intel_backlight/actual_brightness`
if [ $curr -lt 4477 ]; then
  curr=$((curr+406));
  echo $curr  > /sys/class/backlight/intel_backlight/brightness;
fi' > file # use overwrite mode so that you don't keep on appending the same script to that file over and over again, unless that's what you want. 

Usar o seguinte também funciona.

cat <<< ' > file
 ... code ...'

Além disso, é importante notar que ao usar heredocs, << EOFcomo ocorre a substituição e expansão de variável e semelhantes. Então, fazer algo assim:

cat << EOF > file
cd "$HOME"
echo "$PWD" # echo the current path
EOF

sempre resultará na expansão das variáveis $HOMEe $PWD. Portanto, se o seu diretório pessoal for /home/foobare o caminho atual for /home/foobar/bin, fileserá semelhante a:

cd "/home/foobar"
echo "/home/foobar/bin"

em vez do esperado:

cd "$HOME"
echo "$PWD"
Alexej Magura
fonte
2
Obviamente, uma string here entre aspas simples não pode conter aspas simples, o que pode ser um problema proibitivo. Aqui, os documentos são a única solução sã se você tiver um script que precisa conter aspas simples e duplas, embora esse não seja o caso com o exemplo simples do OP. Além disso, tangencialmente, as strings here <<<estão disponíveis apenas a partir do Bash 3, e não são portáveis ​​para outros shells.
tripleee
<<<também está disponível em Zsh
Alexej Magura
3
hoje eu aprendi que você pode fazer com que o nome do arquivo siga imediatamente a abertura do heredoc. Obrigado @AlexejMagura!
chaseadamsio