Dlaczego echo „bar” -n różni się od echa -n „bar”?

10

Porównać

echo -n "bar"

z

echo "bar" -n

Pierwsza robi to , co moim zdaniem powinna robić (drukuje „pasek” bez nowej linii), a druga nie. Czy to błąd, czy z założenia? Dlaczego różni się od wielu programów CLI tym, że nie można przenosić opcji? Na przykład tail -f /var/log/messagesjest dokładnie taki sam jak tail /var/log/messages -f. Czasami robię to drugie, kiedy zapominam, że chciałem tego pierwszego, ponieważ wewnętrznie opcje i argumenty są przestawiane, zwykle przez getopt .

Aktualizacja: tak, początkowo osłabiłem moje pytanie. Usunąłem osłabienie, które musisz przejrzeć historię, aby niektóre odpowiedzi miały sens.

ksenoterracid
źródło
3
Z tego, co widzę, różnica jest natychmiast oczywista. echo -n "bar"daje „ echo "bar" -n
takt

Odpowiedzi:

18

Jak zauważyło większość innych, „-n” jest interpretowane dosłownie, jeśli jest umieszczone gdziekolwiek, ale bezpośrednio po echopoleceniu.

Dawniej wszystkie narzędzia UNIX były takie - szukały opcji dopiero bezpośrednio po nazwie polecenia. Prawdopodobnie BSD lub GNU były pionierami bardziej elastycznego stylu (choć mogę się mylić), ponieważ nawet teraz POSIX określa stary sposób jako poprawny (patrz Wytyczna 9, a także man 3 getoptw systemie Linux). W każdym razie, chociaż większość narzędzi Linuksa używa obecnie nowego stylu, istnieją pewne ograniczenia echo.

Echoto bałagan, pod względem standardów, ponieważ istniały co najmniej dwie zasadniczo sprzeczne wersje, zanim powstało POSIX. Z jednej strony masz styl SYSV, który interpretuje znaki z odwrotnym ukośnikiem, ale poza tym traktuje argumenty dosłownie, nie akceptując żadnych opcji. Z drugiej strony masz styl BSD, który traktuje inicjał -njako specjalny przypadek i generuje absolutnie wszystko inne dosłownie. A ponieważ echojest to wygodne, masz tysiące skryptów powłoki, które zależą od jednego lub drugiego zachowania:

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

Z powodu semantycznego „traktuj wszystko dosłownie” nie można nawet dodać nowej opcji echobez zepsucia rzeczy. Gdyby GNU użył na nim schematu elastycznych opcji, piekło rozpętałoby się.

Nawiasem mówiąc, dla najlepszej kompatybilności pomiędzy implementacjami powłoki Bourne'a, użyj printfraczej niżecho .

ZAKTUALIZOWANO, aby wyjaśnić, dlaczego echow szczególności nie używa elastycznych opcji.

Jander
źródło
upvote, ponieważ jako jedyny wspomniałeś, że jest to oczekiwane zachowanie getopt.
Geoffrey Bachelet,
@Jander Dlaczego echo jest „wstrzymaniem”? Myślę, że to gnu coreutil?
Xenoterracide
@xeno: Zaktualizowałem swoją odpowiedź. Zasadniczo GNU nie chciało nic zepsuć.
Jander
1
Zaakceptowanie opcji po pierwszym nieopcyjnych jest specyficzne dla GNU narzędzi i programów wykorzystujących LIBC „s Getopt udogodnienia. Użytkownik może zawsze uzyskać zachowanie kompatybilne wstecz, ustawiając zmienną środowiskową POSIXLY_CORRECT.
Gilles „SO- przestań być zły”
1
W konkretnym przypadku echoistnieją również implementacje, które rozpoznają -elub -Ejako opcje. Aby uzyskać przenośność, nie zaczynaj od -ani nie używaj czegoś takiego printf %s -e.
Gilles „SO- przestań być zły”
9

Inne odpowiedzi prowadzą do tego, że możesz spojrzeć na stronę podręcznika man, aby zobaczyć, że -n staje się częścią ciągu, który należy echo. Chciałbym jednak zaznaczyć, że bez md5sum łatwo to zbadać i sprawia, że ​​to, co się dzieje, jest nieco mniej tajemnicze.

[11:07:44][dasonk@Chloe:~]: echo -n "bar"
bar[11:07:48][dasonk@Chloe:~]: echo "bar" -n
bar -n
Dason
źródło
3
+1 dokładnie. Jeśli potok nie działa zgodnie z oczekiwaniami, przetestuj każdą część osobno.
Mikel
6

Wow, wyjaśnienia wszystkich są długie.

To takie proste:

-n jest opcją, jeśli pojawia się przed ciągiem, w przeciwnym razie jest to tylko kolejny ciąg do powtórzenia.

Usuń | md5sum i zobaczysz, że wynik jest inny.

Mikel
źródło
5

Z prostej inspekcji nie jest to błąd z założenia ...

echo "bar" -n | md5sum

a3f8efa5dd10e90aee0963052e3650a1

Wypróbuj to polecenie dla siebie:

echo "bar -n" | md5sum

Zauważysz, że powstaje md5

a3f8efa5dd10e90aee0963052e3650a1

Właśnie pomieszałeś użycie -n w echu.

W twoim pierwszym przykładowym kodzie

echo -n "bar" | md5sum

-n jest używane do powiedzenia NIE umieszczaj nowej linii po tym echu.

Twoja druga próbka

echo "bar" -n | md5sum

traktuje -n jak dosłowny tekst.

icasimpan
źródło