web-dev-qa-db-fra.com

Y a-t-il quelque chose de similaire à echo -n dans heredoc (EOF)?

J'écris sur un énorme script de script d'usine qui génère de nombreux scripts de maintenance pour mes serveurs.

Jusqu’à présent, j’écris des lignes qui doivent être écrites sur une ligne avec echo -ne par exemple.

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

cela me génère (s'il y a par exemple 2 serveurs dans mon fichier de configuration) un code comme

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

Tous les autres blocs de code qui n'ont pas besoin de cette écriture en ligne que je produis avec heredocs puisque je les trouve beaucoup mieux à écrire et à maintenir. Par exemple. après le code supérieur, j'ai le reste généré comme

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

Donc, le bloc de code final ressemble à

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

Ma question
Existe-t-il un moyen d’avoir un comportement heredoc similaire à echo -ne sans le symbole de fin de ligne?

11
derHugo

Non, Heredoc se termine toujours par une nouvelle ligne. Mais vous pouvez toujours supprimer la dernière nouvelle ligne, par exemple avec Perl:

cat << 'EOF' | Perl -pe 'chomp if eof'
First line
Second line
EOF
  • -p lit l'entrée par ligne, exécute le code spécifié dans -e et affiche la ligne (éventuellement modifiée)
  • chomp supprime la nouvelle ligne si elle existe, eof renvoie true à la fin du fichier.
9
choroba

Existe-t-il un moyen pour un comportement heredoc similaire à echo -ne sans le symbole de fin de ligne?

La réponse courte est que heredoc et herestrings sont juste construits de cette façon, donc non - here-doc et herestring ajouteront toujours une nouvelle ligne de fin et il n'y a pas d'option ou de méthode native pour le désactiver (peut-être qu'il y aura dans les futures versions de bash?).

Cependant, une façon de l'aborder via des méthodes uniquement bash serait de lire ce que heredoc donne via la méthode standard while IFS= read -r, mais d'ajouter un délai et d'imprimer la dernière ligne via printfsans le retour à la ligne final. Voici ce que l'on pourrait faire avec 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

Ce que vous voyez ici est la boucle while habituelle avec l'approche IFS= read -r line. Cependant, chaque ligne est stockée dans une variable et donc retardée (nous sautons donc l'impression de la première ligne, la stockons et ne commençons à imprimer que lorsque nous avons lu la deuxième ligne). De cette façon, nous pouvons capturer la dernière ligne, et quand à la prochaine itération readrenvoie le statut de sortie 1, nous imprimons la dernière ligne sans nouvelle ligne via printfname__. Verbose? Oui. Mais ça marche:

$ ./strip_heredoc_newline.sh                                      
one
two$ 

Notez que le caractère d'invite $ est avancé car il n'y a pas de nouvelle ligne. En prime, cette solution est portable et POSIX-ish, elle devrait donc fonctionner également avec kshet dashname__.

$ dash ./strip_heredoc_newline.sh                                 
one
two$
7

Je vous recommande d'essayer une approche différente. Plutôt que de reconstituer votre script généré à partir de plusieurs instructions echo et heredocs. Je vous suggère d'essayer d'utiliser un seul heredoc.

Vous pouvez utiliser le développement variable et la substitution de commandes dans un heredoc. Comme dans cet exemple:

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

Je trouve que cela conduit souvent à des résultats finaux beaucoup plus lisibles que la production pièce par pièce.

En outre, il semble que vous ayez oublié la ligne #! au début de vos scripts. La ligne #! est obligatoire dans tous les scripts correctement formatés. Tenter d'exécuter un script sans la ligne #! ne fonctionnera que si vous l'appelez à partir d'un shell fonctionnant avec des scripts mal formatés. Même dans ce cas, il pourrait être interprété par un shell différent de celui souhaité.

6
kasperd

Heredocs se termine toujours par une nouvelle ligne de caractères, mais vous pouvez simplement l'enlever. Le moyen le plus simple que je connaisse est d'utiliser le programme head:

head -c -1 <<EOF | Sudo tee file > /dev/null
my
heredoc
content
EOF
2
David Foerster

Vous pouvez supprimer les nouvelles lignes de la manière qui vous convient, la tuyauterie vers trserait probablement la plus simple. Cela supprimerait toutes les nouvelles lignes, bien sûr.

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

Cependant, l'instruction si que vous construisez ne l'exige pas, l'expression arithmétique (( .. )) devrait fonctionner correctement, même si elle contient des nouvelles lignes.

Alors tu pourrais faire

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

produisant

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

Construire dans une boucle fait toujours moche, cependant. Le || 0 est bien sûr là pour que nous n’ayons pas besoin de cas spécial la première ou la dernière ligne.


En outre, vous avez ce | Sudo tee ... dans chaque sortie. Je pense que nous pourrions nous en débarrasser en ouvrant une redirection une seule fois (avec substitution de processus) et en l'utilisant pour l'impression ultérieure:

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

Et franchement, je pense toujours que la construction de noms de variables à partir de variables du script externe (comme celui-ci \$${name}_E) est un peu moche, et elle devrait probablement être remplacée par un tableau associatif .

1
ilkkachu