Substituce příkazů a procesů
Substituce příkazů
Substituci příkazů jsme popsali v kapitole Podshell: výraz
$(command) se nahradí standardním výstupem příkazu command. Zde doplníme
několik podrobností, které z pohledu expanzí stojí za zmínku.
Koncové znaky nového řádku se odstraňují. Shell z výstupu odřízne všechny znaky nového řádku na konci, jinak by se s výsledkem obtížně pracovalo.
> echo "[$(echo ahoj)]"
[ahoj]
Vnitřní znaky nového řádku ovšem zůstávají, a bez uvozovek je pak rozdělí dělení slov.
> touch a.txt b.txt c.txt
> for f in $(ls); do echo "[${f}]"; done
[a.txt]
[b.txt]
[c.txt]
> for f in "$(ls)"; do echo "[${f}]"; done
[a.txt
b.txt
c.txt]
Substituce lze vnořovat. To je hlavní praktický důvod, proč dáváme přednost
$( ) před zpětnými apostrofy: u zpětných apostrofů bychom museli vnitřní
výskyty escapovat.
> echo $(basename $(dirname /a/b/c.txt))
b
Výsledek prochází dělením slov a expanzí jmen souborů. Platí tedy stejné varování jako u proměnných: obsahuje-li výstup mezery nebo žolíky, bez uvozovek se rozpadne nebo změní.
> touch "soubor s mezerami.txt"
> for f in $(ls); do echo "[${f}]"; done
[soubor]
[s]
[mezerami.txt]
Toto je zároveň důvod, proč se výstup ls nemá zpracovávat cyklem; místo toho
použijeme expanzi jmen souborů:
for f in *
do
echo "[${f}]"
done
Substituce procesů
Substituce procesů je rozšíření Bashe, které umožňuje předat výstup příkazu tam, kde jiný příkaz očekává jméno souboru.
<(command) # vystup command je dostupny jako soubor ke cteni
>(command) # zapis do "souboru" jde na vstup command
Shell příkaz spustí a výraz nahradí jménem souboru, obvykle cestou
k pojmenované rouře v /dev/fd.
> echo <(ls)
/dev/fd/63
Typickým použitím je porovnání výstupů dvou příkazů. Bez substituce procesů bychom museli vytvářet dočasné soubory.
> diff <(sort soubor1.txt) <(sort soubor2.txt)
> diff <(ls adresar1) <(ls adresar2)
Substituce procesů a cykly while read
V kapitole Vstup pomocí read jsou dvě
varianty čtení souboru po řádcích. Varianta s rourou má nepříjemnou vlastnost,
kterou teď můžeme vysvětlit: každá část roury běží v podshellu,
takže proměnné nastavené v těle cyklu po jeho skončení zaniknou.
# NEFUNGUJE: po cyklu je count opet 1
count=1
cat soubor.txt | while read line
do
count=$(( count + 1 ))
done
echo "precteno $(( count - 1 )) radku"
Řešením je přesměrování ze souboru, které jsme už použili, nebo — potřebujeme-li zpracovat výstup příkazu — právě substituce procesů:
count=1
while read line
do
count=$(( count + 1 ))
done < <(grep 'vzor' soubor.txt)
echo "precteno $(( count - 1 )) radku"
Mezera mezi < a <( je podstatná: první znak je přesměrování standardního
vstupu, <( … ) je substituce procesů. Bez mezery by shell četl výraz jako
přesměrování <<.