Pourquoi l'écho "bar" -n est-il différent de l'écho -n "bar"?

10

Comparer

echo -n "bar"

avec

echo "bar" -n

Le premier fait ce que je pense qu'il est censé faire (imprime "bar" sans nouvelle ligne), tandis que le second ne le fait pas. Est-ce un bug ou par conception? Pourquoi est-il différent de nombreux programmes cli en ce sens que vous ne pouvez pas déplacer les options? Par exemple, tail -f /var/log/messagesest exactement le même que tail /var/log/messages -f. Je fais parfois ce dernier quand j'oublie que je voulais le premier, car en interne les options et les arguments sont réorganisés, généralement par getopt .

Mise à jour: oui, j'ai initialement nerf-ed ma question. J'ai supprimé le nerf que vous devrez consulter l'historique pour donner un sens à certaines des réponses.

xénoterracide
la source
3
D'après ce que je vois, la différence est immédiatement évidente. echo -n "bar"donne "bar", tandis que echo "bar" -ndonne "bar -n"
phunehehe

Réponses:

18

Comme la plupart des autres l'ont observé, "-n" est interprété littéralement s'il est placé n'importe où mais immédiatement après la echocommande.

Historiquement, les utilitaires UNIX étaient tous comme ça - ils ne cherchaient des options qu'immédiatement après le nom de la commande. C'est probablement BSD ou GNU qui a été le pionnier du style plus flexible (bien que je puisse me tromper), car même maintenant POSIX spécifie l'ancienne méthode comme correcte (voir la directive 9, et également man 3 getoptsur un système Linux). Quoi qu'il en soit, même si la plupart des utilitaires Linux utilisent de nos jours le nouveau style, il y a des réticences comme echo.

Echoest un gâchis, du point de vue des normes, en ce sens qu'il y avait au moins deux versions fondamentalement conflictuelles en jeu au moment de la création de POSIX. D'une part, vous avez le style SYSV, qui interprète les caractères d'échappement antislash mais traite autrement ses arguments littéralement, n'acceptant aucune option. De l'autre, vous avez le style BSD, qui traite une initiale -ncomme un cas spécial et sort absolument tout le reste littéralement. Et comme echoc'est si pratique, vous avez des milliers de scripts shell qui dépendent d'un comportement ou de l'autre:

echo Usage: my_awesome_script '[-a]' '[-b]' '[-c]' '[-n]'
echo -a does a thing.
echo -b does something else.
echo -c makes sure -a works right.
echo -- DON\'T USE -n -- it\'s not finished! --

En raison de la sémantique "traiter tout littéralement", il est impossible même d'ajouter une nouvelle option echosans casser les choses. Si GNU utilisait le schéma d'options flexibles, l'enfer se déchaînerait.

Par ailleurs, pour une meilleure compatibilité entre les implémentations du shell Bourne, utilisez printfplutôt queecho .

MIS À JOUR pour expliquer pourquoi echoen particulier n'utilise pas d'options flexibles.

Jander
la source
vote positif puisque vous êtes le seul à mentionner que c'est le comportement getopt attendu.
Geoffrey Bachelet
@jander pourquoi l'écho est-il un "holdout"? Je pense que c'est un gnu coreutil?
xenoterracide
@xeno: J'ai mis à jour ma réponse. Fondamentalement, GNU ne voulait pas casser les choses.
Jander
1
L'acceptation des options après la première non-option est spécifique aux utilitaires GNU et aux programmes utilisant les fonctionnalités getopt de GNU libc . L'utilisateur peut toujours obtenir un comportement à compatibilité descendante en définissant la variable d'environnement . POSIXLY_CORRECT
Gilles 'SO- arrête d'être méchant'
1
Pour le cas spécifique de echo, il existe également des implémentations qui reconnaissent -eou -Ecomme options. Pour la portabilité, ne commencez pas par un -ou n'utilisez pas quelque chose comme printf %s -e.
Gilles 'SO- arrête d'être méchant'
9

Les autres réponses arrivent au point que vous pouvez consulter la page de manuel pour voir que -n devient une partie de la chaîne à faire écho. Cependant, je veux juste souligner qu'il est facile d'enquêter sans le md5sum et rend ce qui se passe un peu moins cryptique.

[11:07:44][dasonk@Chloe:~]: echo -n "bar"
bar[11:07:48][dasonk@Chloe:~]: echo "bar" -n
bar -n
Dason
la source
3
+1 exactement. Si un pipeline ne fait pas ce que vous attendez, testez chaque partie séparément.
Mikel
6

Wow, les explications de tout le monde sont longues.

C'est aussi simple que cela:

-n est une option si elle apparaît avant la chaîne, sinon c'est juste une autre chaîne à faire écho.

Supprimez le | md5sum et vous verrez que la sortie est différente.

Mikel
la source
5

De simple inspection, ce n'est pas un bug par conception ...

echo "bar" -n | md5sum

a3f8efa5dd10e90aee0963052e3650a1

Essayez cette commande par vous-même:

echo "bar -n" | md5sum

Vous remarquerez que le md5 résultant est

a3f8efa5dd10e90aee0963052e3650a1

Vous venez de mélanger l'utilisation de -n dans l'écho.

Dans votre premier exemple de code

echo -n "bar" | md5sum

-n est utilisé pour dire NE PAS mettre de nouvelle ligne après cet écho.

Votre deuxième échantillon

echo "bar" -n | md5sum

traite -n comme un texte littéral.

icasimpan
la source