Existe algo semelhante a echo -n em heredoc (EOF)?

9

Estou escrevendo em um roteiro enorme de fábrica de scripts, gerando muitos scripts de manutenção para meus servidores.

Até agora, escrevo algumas linhas que precisam ser escritas em uma linha com echo -ne , por exemplo,

echo -n "if (( " | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null

# Generate exitCode check for each Server
IFS=" "
COUNT=0
while read -r name ipAddr
do
    if(($COUNT != 0))
    then
        echo -n " || " | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null
    fi

    echo -n "($"$name"_E != 0 && $"$name"_E != 1)" | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null

    COUNT=$((COUNT+1))

    JOBSDONE=$((JOBSDONE+1))
    updateProgress $JOBCOUNT $JOBSDONE
done <<< "$(sudo cat /root/.virtualMachines)"

echo " ))" | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null

isso me gera (se houver, por exemplo, dois servidores no meu arquivo de configuração) como

if (( ($server1_E != 0 && $server1_E != 1) || ($server2_E != 0 && $server2_E != 1) ))

Todos os outros blocos de código que não precisam dessa escrita de código in-line eu produzo com heredocs, pois acho que é melhor escrever e manter. Por exemplo. depois do código superior eu tenho o resto gerado como

cat << EOF | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null
then
    # Print out ExitCode legend
    echo " ExitCode 42 - upgrade failed"
    echo " ExitCode 43 - Upgrade failed"
    echo " ExitCode 44 - Dist-Upgrade failed"
    echo " ExitCode 45 - Autoremove failed"
    echo ""
    echo ""
fi
EOF

Assim, o bloco de código final parece

if (( ($server1_E != 0 && $server1_E != 1) || ($server2_E != 0 && $server2_E != 1) ))
then
    # Print out ExitCode legend
    echo " ExitCode 42 - upgrade failed"
    echo " ExitCode 43 - Upgrade failed"
    echo " ExitCode 44 - Dist-Upgrade failed"
    echo " ExitCode 45 - Autoremove failed"
    echo ""
    echo ""
fi

Minha pergunta
Existe uma maneira de ter um heredoc se comportando de maneira semelhante a echo -ne sem o símbolo de fim de linha?

    
por derHugo 30.11.2017 / 09:44

5 respostas

9

Não, heredoc sempre termina em uma nova linha. Mas você ainda pode remover a última nova linha, por exemplo, com Perl:

cat << 'EOF' | perl -pe 'chomp if eof'
First line
Second line
EOF
  • -p lê a linha de entrada por linha, executa o código especificado em -e e imprime a linha (possivelmente modificada)
  • chomp remove a nova linha final se existir, eof retorna verdadeiro no final do arquivo.
por choroba 30.11.2017 / 09:48
7
  

Existe alguma maneira de um heredoc se comportar de maneira semelhante ao echo -ne sem o símbolo de fim de linha?

Resposta curta é que heredoc e herestrings são construídos dessa forma, então não - aqui-doc e herestring estarão adicionando uma nova linha à direita sempre e não há opção ou uma maneira nativa de desativá-la (talvez Haverá no futuro liberações bash?).

No entanto, uma maneira de abordá-lo através de formas bash-only seria ler o que heredoc dá através do método padrão while IFS= read -r , mas adicionar um atraso e imprimir a última linha via printf sem a nova linha. Veja o que se pode fazer com o bash-only:

#!/usr/bin/env bash

while true
do
    IFS= read -r line || { printf "%s" "$delayed";break;}
    if [ -n "$delayed" ]; 
    then
        printf "%s\n" "$delayed"
    fi
    delayed=$line
done <<EOF
one
two
EOF

O que você vê aqui é o usual while loop com a abordagem IFS= read -r line , porém cada linha é armazenada em uma variável e assim atrasada (então pulamos a impressão da primeira linha, armazenamos e começamos a imprimir somente quando lemos segunda linha). Dessa forma, podemos capturar a última linha e, quando estiver na próxima iteração, read retornará o status de saída 1, quando imprimiremos a última linha sem nova linha via printf . Verboso? sim. Mas funciona:

$ ./strip_heredoc_newline.sh                                      
one
two$ 

Observe que o caractere de prompt $ está sendo avançado, já que não há nova linha. Como bônus, esta solução é portátil e POSIX-ish, então deve funcionar em ksh e dash também.

$ dash ./strip_heredoc_newline.sh                                 
one
two$
    
por Sergiy Kolodyazhnyy 30.11.2017 / 10:40
6

Eu recomendo que você tente uma abordagem diferente. Em vez de juntar seu script gerado a partir de várias instruções echo e heredocs. Eu sugiro que você tente usar um único heredoc.

Você pode usar a expansão de variáveis e a substituição de comandos dentro de um heredoc. Como neste exemplo:

#!/bin/bash
cat <<EOF
$USER $(uname)
EOF

Eu acho que isso geralmente leva a resultados finais muito mais legíveis do que produzir a saída peça por peça.

Além disso, parece que você esqueceu a linha #! no início de seus scripts. A linha #! é obrigatória em qualquer script formatado corretamente. A tentativa de executar um script sem a linha #! só funcionará se você chamá-lo de um shell que funcione com scripts mal formatados e, mesmo nesse caso, pode acabar sendo interpretado por um shell diferente do desejado.

    
por kasperd 30.11.2017 / 13:40
1

Você pode remover as novas linhas da maneira que preferir, bastando que tr seja mais simples. Isso removeria todas as novas linhas, é claro.

$ tr -d '\n' <<EOF 
if ((
EOF

Mas, a declaração if que você está construindo não exige isso, a expressão aritmética (( .. )) deve funcionar bem mesmo que contenha novas linhas.

Então, você poderia fazer

cat <<EOF 
if ((
EOF
for name in x y; do 
    cat << EOF
    ( $${name}_E != 0 && $${name}_E != 1 ) ||
EOF
done
cat <<EOF
    0 )) ; then 
    echo do something
fi
EOF

produzindo

if ((
    ( $x_E != 0 && $x_E != 1 ) ||
    ( $y_E != 0 && $y_E != 1 ) ||
    0 )) ; then 
    echo do something
fi

Construí-lo em um loop ainda faz feio, no entanto. O || 0 está lá, é claro, para que não seja necessário especificar a primeira ou a última linha.

Além disso, você tem esse | sudo tee ... em cada saída. Acho que poderíamos nos livrar disso abrindo um redirecionamento apenas uma vez (com a substituição do processo) e usando isso para a impressão posterior:

exec 3> >(sudo tee outputfile &>/dev/null)
echo output to the pipe like this >&3
echo etc. >&3
exec 3>&-         # close it in the end

E, francamente, eu ainda acho que construir nomes de variáveis a partir de variáveis no script externo (como $${name}_E ) é um pouco feio, e provavelmente deve ser substituído por um Arrayassociativo .

    
por ilkkachu 30.11.2017 / 15:03
1

Heredocs sempre terminam com caracteres de nova linha, mas você pode simplesmente remover isso. A maneira mais fácil que eu sei é com o programa head :

head -c -1 <<EOF | sudo tee file > /dev/null
my
heredoc
content
EOF
    
por David Foerster 21.12.2017 / 15:33

Tags