GNU/Linux >> Znalost Linux >  >> Linux

Proč je Printf lepší než Echo?

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 jak vyžaduje POSIX. Specifikace UNIX je přísnější, zakazuje -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í).

Související:Zpracovat každý řádek výstupu z `ping` okamžitě v potrubí?

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 "
Linux
  1. Proč se Tilda (~) nerozšíří uvnitř dvojitých uvozovek?

  2. Proč používat Install Rather than Cp And Mkdir?

  3. Jak zacházet s více než 10 parametry v shellu

  1. echo znak nového řádku nefunguje v bash

  2. Je algoritmus pro kopírování souborů (Ubuntu) Linux lepší než Windows 7?

  3. Proč se heslo 'sudo' liší od hesla 'su root'

  1. [Linux]:Diagnostikujte síťové problémy pomocí MTR – lepší než traceroute!

  2. Proč je Linux VPS lepší volbou než Windows VPS

  3. Proč hardwarový router funguje lépe než router Linux s lepšími specifikacemi (RAM a CPU)?