Por que echo "bar" -n é diferente de echo -n "bar"?

10

Comparar

echo -n "bar"

com

echo "bar" -n

O primeiro faz o que eu acho que deveria fazer (imprime "barra" sem uma nova linha), enquanto o último não. Isso é um bug ou por design? Por que é diferente de muitos programas CLI porque você não pode mover as opções? Por exemplo, tail -f /var/log/messagesé exatamente o mesmo que tail /var/log/messages -f. Às vezes, faço o último quando esqueço que queria o primeiro, porque internamente as opções e argumentos são reorganizados, geralmente por getopt .

Atualização: sim, eu originalmente nerf-ed minha pergunta. Eu removi o nerf, você terá que ver o histórico para fazer com que algumas das respostas façam sentido.

xenoterracida
fonte
3
Pelo que vejo, a diferença é imediatamente óbvia. echo -n "bar"dá "bar", enquanto echo "bar" -ndá "bar -n"
phunehehe

Respostas:

18

Como muitos outros observaram, "-n" é interpretado literalmente se colocado em qualquer lugar, mas imediatamente após o echocomando.

Historicamente, os utilitários UNIX eram todos assim - eles procuravam opções apenas imediatamente após o nome do comando. Provavelmente, o BSD ou o GNU foram os pioneiros no estilo mais flexível (embora eu possa estar errado), pois até agora o POSIX especifica a maneira antiga como correta (consulte a Diretriz 9 e também man 3 getoptem um sistema Linux). De qualquer forma, embora a maioria dos utilitários Linux hoje em dia use o novo estilo, existem alguns exemplos echo.

Echoé uma bagunça, em termos de padrões, em que havia pelo menos duas versões fundamentalmente conflitantes em jogo quando o POSIX surgiu. Por um lado, você tem o estilo SYSV, que interpreta caracteres com escape de barra invertida, mas trata seus argumentos literalmente, não aceitando opções. Por outro lado, você tem o estilo BSD, que trata uma inicial -ncomo um caso especial e gera absolutamente tudo o resto literalmente. E, como echoé tão conveniente, você tem milhares de scripts de shell que dependem de um comportamento ou de outro:

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! --

Por causa da semântica "tratar tudo literalmente", é impossível adicionar uma nova opção echosem quebrar as coisas. Se o GNU usasse o esquema de opções flexíveis, o inferno se abriria.

Aliás, para melhor compatibilidade entre implementações de shell Bourne, use em printfvez deecho .

ATUALIZADO para explicar por que, echoem particular, não usa opções flexíveis.

Jander
fonte
voto positivo desde que você é o único a mencionar que é o comportamento getopt esperado.
Geoffrey Bachelet
@jander Por que o eco é um "holdout"? Eu acho que é um coreutil gnu?
Xenoterracide
@ xeno: Eu atualizei minha resposta. Basicamente, o GNU não queria quebrar as coisas.
Jander
1
Aceitar opções depois de o primeiro não-opção é específica para GNU utilitários e programa usando GNU libc 's getopt instalações. O usuário sempre pode obter um comportamento compatível com versões anteriores, definindo a variável de ambiente POSIXLY_CORRECT.
Gilles 'SO- stop be evil'
1
Para o caso específico de echo, também existem implementações que reconhecem -eou -Ecomo opções. Para portabilidade, não comece com um -ou use algo parecido printf %s -e.
Gilles 'SO- stop be evil'
9

As outras respostas chegam ao ponto em que você pode olhar na página de manual para ver que -n está se tornando parte da cadeia de caracteres para ecoar. No entanto, só quero salientar que é fácil investigar isso sem o md5sum e torna o que está acontecendo um pouco menos enigmático.

[11:07:44][dasonk@Chloe:~]: echo -n "bar"
bar[11:07:48][dasonk@Chloe:~]: echo "bar" -n
bar -n
Dason
fonte
3
+1 exatamente. Se um pipeline não estiver fazendo o que você espera, teste cada parte separadamente.
Mikel
6

Uau, as explicações de todos são longas.

É simples assim:

-n é uma opção se aparecer antes da string, caso contrário, é apenas outra string a ser repetida.

Remova o | md5sum e você verá que a saída é diferente.

Mikel
fonte
5

Da inspeção simples, isso não é um bug por design ...

echo "bar" -n | md5sum

a3f8efa5dd10e90aee0963052e3650a1

Tente este comando por si mesmo:

echo "bar -n" | md5sum

Você notará que o md5 resultante é

a3f8efa5dd10e90aee0963052e3650a1

Você acabou de misturar o uso de -n em eco.

No seu primeiro código de amostra

echo -n "bar" | md5sum

-n é usado para dizer NÃO colocar uma nova linha após esse eco.

Sua segunda amostra

echo "bar" -n | md5sum

está tratando -n como um texto literal.

icasimpan
fonte