Zasadniczo jest to kwestia przenośności (i niezawodności).
Początkowo echo
nie akceptowałem żadnej opcji i niczego nie rozwijałem. Wszystko, co robił, to wypisywanie argumentów oddzielonych znakiem spacji i kończonych znakiem nowej linii.
Teraz ktoś pomyślał, że byłoby miło, gdybyśmy mogli zrobić takie rzeczy, jak echo "\n\t"
wypisywanie znaków nowej linii lub tabulacji, lub mieć opcję, aby nie wypuszczać końcowego znaku nowej linii.
Następnie zastanowili się mocniej, ale zamiast dodania tej funkcji do powłoki (tak jak perl
tam, gdzie podwójne cudzysłowy \t
faktycznie oznaczają znak tabulacji), dodali ją echo
.
David Korn sobie sprawę z błędu i wprowadził nową formę cytatów powłoki: $'...'
który został później kopiowane przez bash
a zsh
ale było zbyt późno do tego czasu.
Teraz, gdy standardowy UNIX echo
otrzymuje argument zawierający dwa znaki \
i t
zamiast wypisywać je, wypisuje znak tabulacji. I gdy tylko zobaczy \c
argument, przestaje wypisywać (więc końcowy znak nowej linii też nie jest wypisywany).
Inni dostawcy powłok / wersji Unixa wybrali to inaczej: dodali -e
opcję rozwijania sekwencji specjalnych i -n
opcję nie wysyłania końcowego znaku nowej linii. Niektóre muszą -E
wyłączyć sekwencje specjalne, niektóre mają, -n
ale nie -e
, lista sekwencji specjalnych obsługiwanych przez jedną echo
implementację niekoniecznie jest taka sama jak obsługiwana przez inną.
Sven Mascheck ma ładną stronę, która pokazuje skalę problemu .
Z powyższych echo
implementacjach że opcje pomocy, tam zazwyczaj nie poparcie --
, aby zaznaczyć koniec opcji (na echo
polecenie wbudowane niektórych non-Bourne podobnych muszli zrobienia i zsh obsługuje -
do tego jednak), więc na przykład, trudno jest wyjście "-n"
z echo
w wiele muszli.
W niektórych muszli jak bash
đ lub ksh93
² lub yash
( $ECHO_STYLE
zmienna), zachowanie nawet zależy powłoki opracowano lub środowiska (GNU echo
"zachowania s będą także zmieniać $POSIXLY_CORRECT
się w środowisku, w wersji 4 , zsh
jest z bsd_echo
opcją niektóre oparte na pdksh z ich posix
opcją lub czy są wywoływane jako sh
czy nie). Tak więc dwa bash
echo
s, nawet z tej samej wersji, bash
nie mają gwarancji, że zachowają się tak samo.
POSIX mówi: jeśli pierwszy argument -n
lub dowolny argument zawiera ukośniki odwrotne, zachowanie jest nieokreślone . bash
echo w tym względzie nie jest POSIX, ponieważ na przykład echo -e
nie jest generowane, -e<newline>
jak wymaga POSIX. Specyfikacja UNIX jest bardziej rygorystyczna, zabrania -n
i wymaga rozszerzania niektórych sekwencji ucieczki, w tym tej, \c
aby zatrzymać wysyłanie.
Te specyfikacje tak naprawdę nie przydają się tutaj, biorąc pod uwagę, że wiele implementacji jest niezgodnych. Nawet niektóre certyfikowane systemy, takie jak macOS 5, nie są zgodne.
Aby naprawdę przedstawić aktualną rzeczywistość, POSIX powinien w rzeczywistości powiedzieć : jeśli pierwszy argument pasuje do ^-([eEn]*|-help|-version)$
rozszerzonego α
wyrażenia regularnego lub dowolny argument zawiera odwrotne ukośniki (lub znaki, których kodowanie zawiera kodowanie znaku odwrotnego ukośnika, tak jak w lokalizacjach używających zestawu znaków BIG5), wówczas zachowanie jest następujące: nieokreślony.
Podsumowując, nie wiesz, co echo "$var"
wyświetli, chyba że możesz upewnić się, że $var
nie zawiera znaków odwrotnego ukośnika i nie zaczyna się od -
. W tym przypadku specyfikacja POSIX mówi nam, abyśmy printf
zamiast tego używali .
Oznacza to, że nie można używać echo
do wyświetlania niekontrolowanych danych. Innymi słowy, jeśli piszesz skrypt i pobiera on dane zewnętrzne (od użytkownika jako argumenty lub nazwy plików z systemu plików ...), nie możesz go użyć echo
do wyświetlenia.
To jest wporządku:
echo >&2 Invalid file.
To nie jest:
echo >&2 "Invalid file: $file"
(Chociaż będzie działać OK z niektórymi (niezgodnymi z UNIX) echo
implementacjami, takimi jak bash
s, gdy xpg_echo
opcja nie została włączona w taki czy inny sposób, jak w czasie kompilacji lub przez środowisko).
file=$(echo "$var" | tr ' ' _)
nie jest w porządku w większości implementacji (wyjątkami są yash
z ECHO_STYLE=raw
(z zastrzeżeniem, że yash
„s zmienne nie mogą posiadać dowolne sekwencje bajtów, więc nie arbitralne nazwy pliku) i zsh
” s echo -E - "$var"
6 ).
printf
, z drugiej strony jest bardziej niezawodny, przynajmniej gdy ogranicza się do podstawowego użycia echo
.
printf '%s\n' "$var"
Wyświetla zawartość $var
znaku, po którym następuje znak nowej linii, niezależnie od tego, jaki znak może zawierać.
printf '%s' "$var"
Wyprowadzi go bez końcowego znaku nowej linii.
Istnieją także różnice między printf
implementacjami. Istnieje rdzeń funkcji określonych przez POSIX, ale istnieje wiele rozszerzeń. Na przykład niektórzy popierają a, %q
aby zacytować argumenty, ale sposób wykonania różni się w zależności od powłoki, niektóre obsługują \uxxxx
znaki Unicode. Zachowanie różni się printf '%10s\n' "$var"
w przypadku wielobajtowych lokalizacji, istnieją co najmniej trzy różne wynikiprintf %b '\123'
Ale w końcu, jeśli trzymasz się zestawu funkcji POSIX printf
i nie próbujesz robić z nim nic szczególnego, nie masz problemów.
Pamiętaj jednak, że pierwszym argumentem jest format, więc nie powinien zawierać zmiennych / niekontrolowanych danych.
Bardziej niezawodny echo
można wdrożyć za pomocą printf
:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%s\n' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%b\n' "$*"
)
Podpowłoki (która implikuje spawnowanie dodatkowego procesu w większości implementacji powłoki) można uniknąć za local IFS
pomocą wielu powłok lub pisząc je w następujący sposób:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
Uwagi
1. jaki bash
jest echo
zachowanie może być zmienione.
W bash
czasie wykonywania istnieją dwie rzeczy, które kontrolują zachowanie echo
(obok enable -n echo
lub przedefiniowanie echo
jako funkcji lub aliasu): xpg_echo
bash
opcja i bash
to, czy jest w trybie POSIX. posix
Tryb można włączyć, jeśli bash
jest wywoływany jako sh
lub POSIXLY_CORRECT
w środowisku lub z posix
opcją:
Domyślne zachowanie w większości systemów:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo
rozwija sekwencje, ponieważ UNIX wymaga:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
Nadal honoruje -n
i -e
(i -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
W xpg_echo
trybie POSIX i:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "\0101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"'
-n A
Tym razem bash
jest zgodny zarówno z POSIX, jak i UNIX. Zauważ, że w trybie POSIX bash
wciąż nie jest zgodny z POSIX, ponieważ nie wyświetla danych wyjściowych -e
:
$ env SHELLOPTS=posix bash -c 'echo -e'
$
Domyślne wartości xpg_echo i posix można zdefiniować w czasie kompilacji za pomocą opcji --enable-xpg-echo-default
i skryptu. Tak zwykle robią ostatnie wersje OS / X, aby je zbudować . Jednak żadne wdrożenie / dystrybucja Uniksa / Linuksa przy zdrowych zmysłach zazwyczaj tego nie zrobiłoby . W rzeczywistości nie jest to prawdą, że Oracle jest dostarczane z Solaris 11 (w pakiecie opcjonalnym), jak się wydaje (tak nie było w Solarisie 10).--enable-strict-posix-default
configure
/bin/sh
/bin/bash
/bin/bash
--enable-xpg-echo-default
2. Jak ksh93
jest echo
zachowanie może być zmienione.
W ksh93
, czy echo
rozwija sekwencje specjalne czy nie i rozpoznaje opcje, zależy od zawartości zmiennych środowiskowych $PATH
i / lub $_AST_FEATURES
.
Jeśli $PATH
zawiera komponent, który zawiera /5bin
lub /xpg
przed komponentem /bin
lub /usr/bin
, wówczas zachowuje się w sposób SysV / UNIX (rozwija sekwencje, nie akceptuje opcji). Jeśli znajdzie /ucb
lub /bsd
pierwszy lub jeśli $_AST_FEATURES
7 zawiera UNIVERSE = ucb
, zachowuje się w BSD 3 sposób ( -e
aby umożliwić ekspansję, rozpoznaje -n
).
Domyślnie jest zależne od systemu, BSD na Debianie (zobacz dane wyjściowe builtin getconf; getconf UNIVERSE
w najnowszych wersjach ksh93):
$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD
3. BSD dla echa -e?
Odniesienie do BSD do obsługi -e
opcji jest tutaj nieco mylące. Większość tych różnych i niekompatybilnych echo
zachowań została wprowadzona w AT&T:
\n
, \0ooo
, \c
W programisty stole warsztatowym UNIX (w oparciu o Unix V6), a reszta ( \b
, \r
...) w System III sygn .
-n
w Unix V7 (autor: Dennis Ritchie Ref )
-e
w Unix V8 (autor: Dennis Ritchie Ref )
-E
sam prawdopodobnie początkowo pochodzi z bash
(CWRU / CWRU.chlog w wersji 1.13.5 wspomina Briana Foxa dodającego go w dniach 1992-10-18, GNU echo
kopiuje go wkrótce w sh-utils-1.8 wydanym 10 dni później)
Chociaż echo
wbudowane sh
lub BSD są obsługiwane -e
od dnia, w którym zaczęły używać powłoki Almquist na początku lat 90., samodzielne echo
narzędzie do dziś tego nie obsługuje ( FreeBSDecho
nadal nie obsługuje -e
, chociaż obsługuje -n
takie jak Unix V7 (i także, \c
ale tylko na końcu ostatniego argumentu)).
Obsługa -e
została dodana do ksh93
, echo
gdy we wszechświecie BSD w wersji ksh93r wydanej w 2006 roku i można ją wyłączyć w czasie kompilacji.
4. Zmiana zachowania echa GNU w 8.31
Od coreutils 8,31 (i to popełniają ), GNU echo
teraz rozszerza sekwencje domyślnie po POSIXLY_CORRECT w środowisku, aby dopasować zachowanie bash -o posix -O xpg_echo
„s echo
polecenie wbudowane (patrz raport o błędzie ).
5. macOS echo
Większość wersji macOS otrzymała certyfikat UNIX od OpenGroup .
Ich sh
wbudowane echo
jest zgodne, ponieważ jest bash
(bardzo stara wersja) wbudowane z xpg_echo
domyślnie włączoną, ale ich samodzielne echo
narzędzie nie jest. env echo -n
wypisuje nic zamiast -n<newline>
, env echo '\n'
wypisuje \n<newline>
zamiast <newline><newline>
.
To /bin/echo
jest ten z FreeBSD, który tłumi wyjście nowej linii, jeśli pierwszy argument to -n
lub (od 1995), jeśli ostatni argument się kończy \c
, ale nie obsługuje żadnych innych sekwencji odwrotnego ukośnika wymaganych nawet przez UNIX \\
.
6. echo
implementacje, które mogą generować dowolne dane dosłownie
Ściśle mówiąc, można również liczyć, że FreeBSD / macOS /bin/echo
powyżej (nie jest to echo
wbudowane w ich powłokę ), gdzie można zapisać zsh
' echo -E - "$var"
lub yash
' ECHO_STYLE=raw echo "$var"
( s printf '%s\n' "$var"
):
/bin/echo "$var
\c"
Implementacje, które obsługują -E
i -n
(lub można je skonfigurować) mogą również wykonywać:
echo -nE "$var
"
I zsh
„s echo -nE - "$var"
( printf %s "$var"
) można zapisać
/bin/echo "$var\c"
7. _AST_FEATURES
i ASTUNIVERSE
Nie _AST_FEATURES
jest przeznaczony do bezpośredniej manipulacji, służy do propagowania ustawień konfiguracji AST podczas wykonywania poleceń. Konfiguracja ma zostać wykonana za pomocą (nieudokumentowanego) astgetconf()
interfejsu API. Wewnątrz ksh93
The getconf
wbudowane (uruchamiana z builtin getconf
lub powołując command /opt/ast/bin/getconf
) jest interfejsemastgetconf()
Na przykład powinieneś builtin getconf; getconf UNIVERSE = att
zmienić UNIVERSE
ustawienie na att
(powodując echo
między innymi zachowanie SysV). Po wykonaniu tej czynności zauważysz, że $_AST_FEATURES
zmienna środowiskowa zawiera UNIVERSE = att
.
echo -e
?