Ladění a typické chyby
Většinu chyb spojených s expanzemi lze odhalit tím, že se podíváme, co shell po expanzi doopravdy spouští.
Jak se podívat na výsledek expanze
set -x vypisuje na stderr každý příkaz po expanzi, uvozený promptem
PS4 (výchozí hodnota je +). Podrobněji viz podkapitola
set.
> set -x
> FOO="muj soubor.txt"
+ FOO='muj soubor.txt'
> ls ${FOO}
+ ls muj soubor.txt
ls: cannot access 'muj': No such file or directory
> ls "${FOO}"
+ ls 'muj soubor.txt'
Ve výpisu je vidět přesně to podstatné: v prvním případě dostal ls dva
argumenty, ve druhém jeden. Uvozovky ve výpisu set -x nejsou naše původní
uvozovky, ale shellem doplněné vyznačení hranic argumentů.
printf '%s\n' vypíše každý argument na vlastní řádek, takže je okamžitě
vidět, na kolik slov se expanze rozpadla. Je to spolehlivější než echo, které
argumenty spojí mezerami.
> FOO="a b"
> printf '[%s]\n' ${FOO}
[a]
[b]
> printf '[%s]\n' "${FOO}"
[a b]
declare -p vypíše proměnnou i s uvozovkami a u polí i s indexy, takže je
poznat, co v ní opravdu je.
> pole=("prvni prvek" druhy)
> declare -p pole
declare -a pole=([0]="prvni prvek" [1]="druhy")
Pro vzory je užitečné vyzkoušet si expanzi napřed pomocí echo, než ji použijeme
v destruktivním příkazu:
> echo rm -f *.tmp
rm -f a.tmp b.tmp
Typické chyby
-
Neuvozená proměnná se jménem obsahujícím mezeru.* Nejčastější chyba vůbec.
cp ${zdroj} ${cil} # spatne cp "${zdroj}" "${cil}" # spravne -
Neuvozená proměnná obsahující žolík. Hodnota se po expanzi promění na seznam souborů, které s ní nemají nic společného.
> VZOR='*' > echo ${VZOR} a.txt b.txt foto1.jpg > echo "${VZOR}" * -
Prázdná proměnná zmizí jako argument. Test pak dostane jiný počet argumentů, než čekáme.
if [ -n ${x} ] # spatne: pri prazdnem x je to test [ -n ], vzdy pravdivy if [ -n "${x}" ] # spravne -
rm -rf ${adresar}/*s prázdnou proměnnou. Pokud jeadresarprázdná nebo nenastavená, expanduje se výraz na/*se známým následkem. Obranou je test na začátku skriptu, nebo${adresar:?}, které skript při nenastavené proměnné ukončí (viz Expanze parametrů).rm -rf "${adresar:?adresar neni nastaven}"/*Užitečná je také volba
set -u, po které je použití nenastavené proměnné chybou místo tichého dosazení prázdného řetězce. -
Cyklus přes vzor, který se s ničím neshoduje. Popsáno v kapitole Expanze jmen souborů; řešením je
shopt -s nullglobnebo test existence v těle cyklu. -
Cyklus přes
$(ls). Rozpadne se na jménech s mezerami a expanduje žolíky ve jménech. Místo toho se cyklí přímo přes vzor:for f in $(ls) # spatne for f in * # spravne -
readbez-r. Bez tohoto přepínačereadinterpretuje zpětná lomítka na vstupu jako escapování, takže se ztratí. Ve skriptech proto čteme řádky pomocíwhile IFS= read -r line do echo "${line}" done < soubor.txtPrázdné
IFS=navíc zabrání oříznutí mezer na začátku a na konci řádku. -
Nesprávné pořadí expanzí. Typicky pokus o rozsah s proměnnou mezí (
{1..${N}}) nebo vlnovku v hodnotě proměnné. Obojí je popsáno u příslušných expanzí; společná příčina je, že daná fáze proběhla dřív, než vznikla hodnota, kterou by potřebovala.
Kontrola skriptů
Většinu uvedených chyb umí najít statický analyzátor
ShellCheck, který je v distribucích běžně
k dispozici jako balík shellcheck. Jeho používání vřele doporučuji.
> shellcheck script.sh
In script.sh line 3:
cp ${zdroj} ${cil}
^-------^ SC2086: Double quote to prevent globbing and word splitting.