V mém prostředí Bash používám proměnné obsahující mezery a tyto proměnné používám v rámci substituce příkazů.
Jaký je správný způsob citování mých proměnných? A jak to mám udělat, pokud jsou vnořené?
DIRNAME=$(dirname "$FILE")
nebo cituji mimo substituci?
DIRNAME="$(dirname $FILE)"
nebo obojí?
DIRNAME="$(dirname "$FILE")"
nebo mám použít back-ticks?
DIRNAME=`dirname "$FILE"`
Jaký je správný způsob, jak to udělat? A jak mohu snadno zkontrolovat, zda jsou uvozovky nastaveny správně?
Přijatá odpověď:
V pořadí od nejhoršího k nejlepšímu:
DIRNAME="$(dirname $FILE)"
nebude dělat to, co chcete, pokud$FILE
obsahuje mezery (nebo jakékoli jiné znaky$IFS
aktuálně obsahuje) nebo globbing znaky[?*
.DIRNAME=`dirname "$FILE"`
je technicky správná, ale zpětná zaškrtnutí se pro rozšiřování příkazů nedoporučují kvůli mimořádné složitosti při jejich vnořování a zvláštnímu zpracování zpětných lomítek, ke kterému v nich dochází.DIRNAME=$(dirname "$FILE")
je správné, ale pouze proto, že se jedná o přiřazení ke skalární (nikoli maticové) proměnné. Pokud použijete substituci příkazu v jakémkoli jiném kontextu, napříkladexport DIRNAME=$(dirname "$FILE")
nebodu $(dirname -- "$FILE")
, nedostatek uvozovek způsobí potíže, pokud výsledek rozšíření bude obsahovat mezery nebo kulovité znaky.DIRNAME="$(dirname "$FILE")"
(kromě chybějícího--
, viz níže) je doporučený způsob.DIRNAME=
můžete nahradit s příkazem a mezerou beze změny čehokoli jiného adirname
přijme správný řetězec.
Chcete-li se ještě zlepšit:
DIRNAME="$(dirname -- "$FILE")"
funguje, pokud$FILE
začíná pomlčkou.DIRNAME="$(dirname -- "$FILE" && printf x)" && DIRNAME="${DIRNAME%?x}" || exit
funguje, i když$FILE
's dirname končí novým řádkem, protože$()
odřízne nové řádky na konci výstupu, oba ten přidanýdirname
a ty, které mohou být součástí skutečných dat.
Rozšíření příkazů můžete vnořovat, jak chcete. Pomocí $()
vždy vytvoříte nový kontext citací, takže můžete dělat věci jako toto:
foo "$(bar "$(baz "$(ban "bla")")")"
Vy ne chcete to zkusit pomocí zpětných znamének.