Slyšel jsem, že printf
je lepší než echo
. Vybavuji si pouze jeden příklad ze své zkušenosti, kdy jsem musel použít printf
protože echo
nefungovalo pro vkládání textu do nějakého programu na RHEL 5.8, ale printf
dělal. Ale zjevně existují další rozdíly a rád bych se zeptal, jaké to jsou, a také jestli existují konkrétní případy, kdy použít jeden vs druhý.
Přijatá odpověď:
V zásadě jde o problém přenositelnosti (a spolehlivosti).
Zpočátku echo
nepřijal žádnou možnost a nic nerozšířil. Jediné, co dělal, bylo vypisování argumentů oddělených mezerou a ukončených znakem nového řádku.
Někdo si teď myslel, že by bylo hezké, kdybychom mohli dělat věci jako echo "nt"
pro výstup znaků nového řádku nebo tabulátoru nebo možnost nevypisovat znak nového řádku na konci.
Pak se zamysleli tvrději, ale místo toho, aby tuto funkci přidali do shellu (jako perl
kde uvnitř dvojitých uvozovek t
ve skutečnosti znamená znak tabulátoru), přidali jej do echo
.
David Korn si chybu uvědomil a zavedl novou formu uvozovek:$'...'
který byl později zkopírován bash
a zsh
ale tou dobou už bylo příliš pozdě.
Nyní, když standardní UNIX echo
obdrží argument, který obsahuje dva znaky a
t
, namísto jejich výstupu vypíše znak tabulátoru. A jakmile uvidí c
v argumentu zastaví výstup (takže se nevypíše ani nový řádek na konci).
Ostatní shelly/dodavatelé/verze Unixu se rozhodli to udělat jinak:přidali -e
možnost rozšířit sekvence escape a -n
možnost nevypisovat koncový nový řádek. Některé mají -E
pro deaktivaci escape sekvencí mají některé -n
ale ne -e
, seznam escape sekvencí podporovaných jedním echo
implementace nemusí být nutně stejná jako podporovaná jinou.
Sven Mascheck má pěknou stránku, která ukazuje rozsah problému.
Na těch echo
implementací, které podporují možnosti, obecně neexistuje podpora --
pro označení konce možností (echo
vestavěné do některých shellů jiných než Bourne ano a zsh podporuje -
pro to však), takže je například obtížné vypsat "-n"
s echo
v mnoha skořápkách.
Na některých shellech, jako je bash
¹ nebo ksh93
² nebo yash
($ECHO_STYLE
proměnná), chování dokonce závisí na tom, jak byl shell zkompilován nebo na prostředí (GNU echo
Chování se také změní, pokud $POSIXLY_CORRECT
je v prostředí a s verzí zsh
s jeho bsd_echo
možnost, některé založené na pdksh s jejich posix
nebo zda se nazývají sh
nebo ne). Takže dva bash
echo
s, dokonce ze stejné verze bash
není zaručeno, že se budou chovat stejně.
POSIX říká:pokud je první argument -n
nebo jakýkoli argument obsahuje zpětná lomítka, pak je chování nespecifikováno . bash
echo v tomto ohledu není POSIX v tom, že například echo -e
nevypisuje -e
-n
a vyžaduje rozšíření některých escape sekvencí včetně c
jeden pro zastavení výstupu.
Tyto specifikace zde ve skutečnosti nepřicházejí k záchraně, protože mnoho implementací není v souladu. Dokonce i některé certifikované systémy jako macOS nejsou kompatibilní.
Aby skutečně reprezentoval současnou realitu, měl by POSIX ve skutečnosti říkat:pokud se první argument shoduje s ^-([eEn]*|-help|-version)$
rozšířený regulární výraz nebo jakýkoli argument obsahuje zpětná lomítka (nebo znaky, jejichž kódování obsahuje kódování znaku zpětného lomítka jako α
v národních prostředích používajících znakovou sadu BIG5), pak je chování nespecifikováno.
Celkově vzato, nevíte, co echo "$var"
vypíše, pokud se nemůžete ujistit, že $var
neobsahuje znaky zpětného lomítka a nezačíná -
. Specifikace POSIX nám ve skutečnosti říká, abychom použili printf
místo toho v tom případě.
To tedy znamená, že nemůžete použít echo
k zobrazení nekontrolovaných dat. Jinými slovy, pokud píšete skript a přijímá externí vstup (od uživatele jako argumenty nebo názvy souborů ze systému souborů…), nemůžete použít echo
jej zobrazit.
To je v pořádku:
echo >&2 Invalid file.
Toto není:
echo >&2 "Invalid file: $file"
(I když to bude fungovat dobře s některými (nekompatibilními s UNIX) echo
implementace jako bash
je, když xpg_echo
volba nebyla povolena tak či onak, jako v době kompilace nebo prostřednictvím prostředí).
file=$(echo "$var" | tr ' ' _)
není ve většině implementací v pořádku (výjimkou jsou yash
s ECHO_STYLE=raw
(s upozorněním, že yash
Proměnné 's nemohou obsahovat libovolné sekvence bajtů, takže ne libovolné názvy souborů) a zsh
‘s echo -E - "$var"
).
printf
, na druhou stranu je spolehlivější, alespoň když je omezen na základní použití echo
.
printf '%sn' "$var"
Vypíše obsah $var
následovaný znakem nového řádku bez ohledu na to, jaký znak může obsahovat.
printf '%s' "$var"
Vypíše jej bez znaku nového řádku na konci.
Nyní jsou také rozdíly mezi printf
implementací. Existuje jádro funkcí, které specifikuje POSIX, ale pak existuje spousta rozšíření. Některé například podporují %q
citovat argumenty, ale jak se to dělá, se liší od shellu k shellu, některé podporují uxxxx
pro znaky Unicode. Chování se liší pro printf '%10sn' "$var"
ve vícebajtových národních prostředích existují alespoň tři různé výsledky pro printf %b '123'
Ale nakonec, pokud se budete držet sady funkcí POSIX printf
a nesnažte se s tím dělat nic moc přepychového, máte po problému.
Pamatujte však, že prvním argumentem je formát, takže by neměl obsahovat proměnná/nekontrolovaná data.
Spolehlivější echo
lze implementovat pomocí printf
, jako:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
Podskořápce (což ve většině implementací shellu znamená vytvoření dalšího procesu) se lze vyhnout pomocí lokálního IFS
s mnoha shelly, nebo tak, že to napíšete jako:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
if [ "$#" -gt 0 ]; then
printf ' %s' "[email protected]"
fi
fi
printf 'n'
}
Poznámky
1. jak bash
echo
chování lze změnit.
S bash
, za běhu jsou dvě věci, které řídí chování echo
(vedle povolit -n echo
nebo předefinování echo
jako funkce nebo alias):
xpg_echo
bash
a zda bash
je v režimu posix. posix
režim lze povolit, pokud bash
se nazývá sh
nebo pokud POSIXLY_CORRECT
je v prostředí nebo s posix
možnost:
Výchozí chování na většině systémů:
$ bash -c 'echo -n "
Bezpečnostní důsledky zapomenutí citovat proměnnou ve skořápkách Bash/posix?
Proč *ne* analyzovat `ls` (a co dělat místo toho)?
Linux