Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 je adresar prá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 nullglob nebo 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
    
  • read bez -r. Bez tohoto přepínače read interpretuje 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.txt
    

    Prá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.