Comment sortir une chaîne multiligne dans Bash?


250

Comment puis-je sortir une chaîne multipline dans Bash sans utiliser plusieurs appels d'écho comme ceci:

echo "usage: up [--level <n>| -n <levels>][--help][--version]"
echo 
echo "Report bugs to: "
echo "up home page: "

Je suis à la recherche d'un moyen portable de le faire, en utilisant uniquement les modules internes Bash.


4
Si vous émettez un message d'utilisation en réponse à une invocation incorrecte, vous enverriez normalement ce message à l'erreur standard au lieu de la sortie standard, avececho >&2 ...
Mark Reed

2
@MarkReed Le message d'utilisation est sorti en tapant --help(qui devrait aller à la sortie standard).
helpermethod

Pour ceux qui viennent, plus d'informations sur "ici les documents" sont disponibles: tldp.org/LDP/abs/html/here-docs.html
Jeffrey Martinez

Vérifiez la printfsolution basée sur Gordon Davidson. Bien qu'il soit dans l'ombre des approches echoou catbasées, il semble être beaucoup moins compliqué. Certes, la syntaxe `printf 'représente un peu une courbe d'apprentissage, mais j'aimerais
entendre

Réponses:


296

Ici, les documents sont souvent utilisés à cette fin.

cat << EOF
usage: up [--level <n>| -n <levels>][--help][--version]

Report bugs to: 
up home page:
EOF

Ils sont pris en charge dans tous les shells dérivés de Bourne, y compris toutes les versions de Bash.


4
Ouais - mais ce catn'est pas intégré.
Mark Reed

8
@MarkReed: C'est vrai, mais il est toujours disponible (sauf éventuellement dans des circonstances inhabituelles).
pause jusqu'à nouvel ordre.

6
+1 Thx. J'ai fini par utiliser read -d '' help <<- EOF ...pour lire la chaîne multiligne dans une variable, puis j'ai fait écho au résultat.
helpermethod

3
puis-je enregistrer un HEREDOC dans une variable?
chovy

177

ou vous pouvez le faire:

echo "usage: up [--level <n>| -n <levels>][--help][--version]

Report bugs to: 
up home page: "

1
@OliverWeiler: Cela fonctionnera même dans les shells Bourne tels que Dash et Heirloom Bourne Shell .
pause jusqu'à nouvel ordre.

6
Pas génial si vous en avez besoin dans une fonction, car vous devrez soit 1) dépasser la chaîne complètement à gauche de votre fichier ou 2) le garder en retrait pour s'aligner avec le reste de votre code, mais il imprime avec les retraits aussi
sg

43

Inspiré par les réponses perspicaces de cette page, j'ai créé une approche mixte, que je considère comme la plus simple et la plus flexible. Qu'est-ce que tu penses?

Tout d'abord, je définis l'utilisation dans une variable, ce qui me permet de la réutiliser dans différents contextes. Le format est très simple, presque WYSIWYG, sans avoir besoin d'ajouter de caractères de contrôle. Cela me semble raisonnablement portable (je l'ai exécuté sur MacOS et Ubuntu)

__usage="
Usage: $(basename $0) [OPTIONS]

Options:
  -l, --level <n>              Something something something level
  -n, --nnnnn <levels>         Something something something n
  -h, --help                   Something something something help
  -v, --version                Something something something version
"

Ensuite, je peux simplement l'utiliser comme

echo "$__usage"

ou encore mieux, lors de l'analyse des paramètres, je peux simplement y faire écho dans une ligne:

levelN=${2:?"--level: n is required!""${__usage}"}

4
cela a fonctionné pour moi dans un script où la réponse ci-dessus ne fonctionne pas (sans modification).
David Welch

3
C'est beaucoup plus propre que d'impliquer un tas de caractères comme \ t et \ n qui sont difficiles à trouver dans le texte et à développer pour rendre la sortie très différente de la chaîne dans le script
sg

1
Pour certaines raisons, il imprime tout sur la même ligne pour moi: /
Nicolas de Fontenay

2
@Nicolas: L'utilisation de guillemets doubles echo "$__usage"était nécessaire pour moi. echo $__usagen'a pas marché.
Mario

24

Utilisez l' -eoption, puis vous pouvez imprimer un nouveau caractère de ligne avec \ndans la chaîne.

Échantillon (mais je ne sais pas s'il est bon ou non)

Ce qui est amusant, c'est que cette -eoption n'est pas documentée dans la page de manuel de MacOS alors qu'elle est toujours utilisable. Il est documenté dans la page de manuel de Linux .


6
Ces pages de manuel sont destinées à la echocommande fournie par le système /bin/echo, qui sur Mac OS n'a pas d' -eoption. lorsque vous utilisez bash sur ces systèmes, sa echocommande intégrée prend le relais. Vous pouvez le voir en tapant explicitement /bin/echo whateveret en observant la différence de comportement. Pour voir la documentation du intégré, tapez help echo.
Mark Reed

1
/bin/echoest souvent différent d'un OS à l'autre et différent de la fonction intégrée de Bash echo.
pause jusqu'à nouvel ordre.

@MarkReed: J'essaierai plus tard, mais merci pour l'info. +1. Je vais laisser ma réponse ici, car il y a beaucoup de bonnes discussions en cours.
nhahtdh

7
echo -en'est pas portable - par exemple, certaines implémentations d'écho afficheront le "-e" dans le cadre de la sortie. Si vous souhaitez la portabilité, utilisez plutôt printf. Par exemple, / bin / echo sur OS X 10.7.4 fait cela. L'écho intégré de l'IIRC était également bizarre sous 10.5.0, mais je ne me souviens plus des détails.
Gordon Davisson

2
echo -em'a mordu avant ... Certainement utiliser printfou catavec un hérédoc. La <<-variante de la documentation ici est particulièrement agréable car vous pouvez supprimer l'indentation principale dans la sortie, mais le retrait pour la lisibilité dans le script
zbeekman

22

Comme je l'ai recommandé printfdans un commentaire, je devrais probablement donner quelques exemples de son utilisation (bien que pour imprimer un message d'utilisation, je serais plus susceptible d'utiliser les réponses de Dennis ou Chris). printfest un peu plus complexe à utiliser que echo. Son premier argument est une chaîne de format, dans laquelle les échappements (comme \n) sont toujours interprétés; il peut également contenir des directives de format commençant par %, qui contrôlent où et comment tous les arguments supplémentaires y sont inclus. Voici deux approches différentes pour l'utiliser pour un message d'utilisation:

Tout d'abord, vous pouvez inclure l'intégralité du message dans la chaîne de format:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: \nup home page: \n"

Notez que contrairement à echo, vous devez inclure explicitement la nouvelle ligne finale. De plus, si le message contient des %caractères, ils doivent être écrits sous la forme %%. Si vous souhaitez inclure les adresses de rapport de bug et de page d'accueil, vous pouvez les ajouter tout naturellement:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: %s\nup home page: %s\n" "$bugreport" "$homepage"

Deuxièmement, vous pouvez simplement utiliser la chaîne de format pour lui faire imprimer chaque argument supplémentaire sur une ligne distincte:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: " "up home page: "

Avec cette option, l'ajout des adresses de rapport de bogue et de page d'accueil est assez évident:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: $bugreport" "up home page: $homepage"

9

De plus, avec le code source en retrait, vous pouvez utiliser <<-(avec un tiret de fin) pour ignorer les tabulations en tête (mais pas les espaces en tête). Par exemple ceci:

if [ some test ]; then
    cat <<- xx
        line1
        line2
xx
fi

Génère du texte en retrait sans le premier espace blanc:

line1
line2

Cela n'a pas fonctionné pour moi. Quel shell utilisez-vous?
four43

N'a pas fonctionné dans bash 4.4.19 dans Ubuntu. Il n'a pas supprimé l'espacement avant la ligne 1 et la ligne 2
four43

1
@ four43, vous aviez raison. Ne fonctionne pas pour supprimer les espaces de début. Cependant, supprime les onglets de tête. J'ai donc corrigé ma réponse des tabulations et des espaces, aux tabulations et non aux espaces. Désolé pour l'erreur. J'ai vérifié le manuel et il indique clairement que les onglets sont supprimés. Merci d'avoir porté cela à mon attention.
Vue elliptique le

0

Si vous utilisez la solution de @jorge et constatez que tout est sur la même ligne, assurez-vous de placer la variable entre guillemets:

echo $__usage

imprimera tout sur une seule ligne alors que

echo "$__usage"

conservera les nouvelles lignes.


C'est en fait la seule solution qui a fonctionné pour moi. Printf fait beaucoup de choses et avec mon XML multiligne, il nécessite probablement beaucoup d'échappements car il gâche complètement le contenu. J'attribue foo = cat <<EOF .... EOF&& echo "$ foo"
Jilles van Gurp
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.