Hrátky z řádky: Návratové hodnoty

Ondřej Bojar 3. 3. 2008

Opět se setkáváme u pravidelné pondělní dávky tipů a triků z černé řádky. Minulé díly nás naučily přesměrovávat vstupy a výstupy, příkazovou řádku a proměnné do roury a opačně. Dnešní díl se podívá podrobněji na návratovou hodnotu procesu a kombinace procesů právě na základě návratových hodnot.

Dva příkazy za sebou

Filozofii příkazové řádky v Unixu už určitě tušíte: programy jsou stavební kameny, z nichž budujete větší celky. Podobně jako funkce (procedury, rutiny, predikáty, podle vašeho oblíbeného nářečí) pospojované za sebe tvoří program.

Nejsnazší způsob, jak „kombinovat“ dva programy, je spustit jeden a pak druhý. Když to celé chcete zapsat na jednu řádku (a nemačkat Enter/Return) dvakrát, můžete jako oddělovač použít středník:

$ tar czf adresar.tgz adresar; rm -rf adresar
$ # takhle je to ale riskantní! Čtěte dál.

Program tar (výklad mírně kryptických parametrů viz manuálová stránka) zabalí strom adresáře adresar do souboru adresar.tgz. Program rm pak rozbalený originál smaže.

Co když ale tar úkol nezvládne? Třeba když dojde místo na disku… Středník tohle neřeší a po taru prostě spustí mazání.

Konjunkce: Pokračuj jen při úspěchu

Obecně doporučuji na středník (nebo prostý nový řádek ve skriptech) skoro zapomenout a vždy dávat přednost konjunkci &&:

$ tar czf adresar.tgz adresar && rm -rf adresar
$ # takhle ano

Konjunkce bashi nařizuje ověřit návratovou hodnotu taru a spustit rm, jen pokud tar signalizoval úspěch (exit code roven 0).

Disjunkce: Záchranné řešení při neúspěchu

Disjunkce ( ||) se velmi často hodí pro předčasné ukončení skriptu, kde by bylo velmi nepohodlné příkazy stále spojovat pomocí &&. (Skript není nic jiného než textový soubor s řadou příkazů, více si povíme někdy příště.)

$ tar czf adresar.tgz adresar || exit 1
$ # jestli tar selže, ukonči skript
$ rm -rf adresar   # chyba při mazání nám tolik nevadí

Příkaz exit ukončí aktuální shell (tj. v případě skriptu ukončí skript) s udanou návratovou hodnotou. Příklad výše tedy signalizuje neúspěch (exit code 1); exit bez parametru skončí s úspěchem (exit code 0). Podobně jako by vaše prsty měly místo středníku skoro automaticky psát &&, měly by za každý příkaz ve skriptu hned psát exit a nezapomenout jedničku. Jinak si totiž vybudujete sbírku nespolehlivých nástrojů, které vás podrazí v nejméně čekaných chvílích.

Složité sestavy konjunkcí a disjunkcí člověk v praxi většinou nestaví, příliš snadno se v nich lze zamotat a navíc jsou po delší době nečitelné. Tomu, kdo by se do toho přece jen chtěl pustit, připomínám existenci složených závorek pro uzávorkování výrazů. Je ale nutné psát závorky oddělené mezerami a středníkem od okolních příkazů, např.:

$ { { prikaz1 || prikaz2; } && prikaz 3; }; prikaz 4

Spuštění na pozadí

Ukažme si ještě, co způsobí malý překlep: & místo &&:

$ tar czf adresar.tgz adresar & rm -rf adresar
$ # takhle rozhodně ne!!!

Jeden ampersand jako oddělovač příkazů znamená: spusť první, nečekej na nic a hned spusť druhý. Co myslíte, když si komprimace a mazání na vašem počítači dají závody, kdo vyhraje?

Ampersand můžete chápat jako oddělovač příkazů, alternativu ke středníku: středník znamená „spusť jeden po druhém“ ampersand „spusť současně“ Skoro běžnější je ale chápat ampersand jako příznak „spusť na pozadí“ který prostě přidáváte k jednomu příkazu:

$ rm -rf adresar &
$ # tohle mazání poběží dlouho, nechci čekat

Při spuštění na pozadí je trochu nemilé, že se návratová hodnota nenávratně ztratí. O tom, jestli mazání nakonec uspělo nebo ne se dozvíte jen podle hlášky, kterou bash časem po skončení procesu vypíše na konzoli: buď Done nebo Exit. Nijak na ni ale ve skriptu reagovat nemůžete, na to je nutné, aby proces na pozadí před svým koncem návratovou hodnotu na smluvené místo poznamenal.

Jak zjistit návratovou hodnotu

Zatím ale neumíme návratovou hodnotu ani zjistit, natož někam poznamenat. Pojďme to napravit:

$ true; echo $?
$ false; echo $?

Programy true a false mají snadný úkol, jen skončit s příslušnou návratovou hodnotou: true signalizuje úspěch, tj. návratová hodnota 0, false neúspěch, proto 1. Speciální proměnná $? obsahuje návratovou hodnotu posledního příkazu. Schválně, co vypíše dvojice:

$ echo $?; echo $?

Návratová hodnota skriptu na pozadí

Vraťme se k problému, jak neztratit návratovou hodnotu příkazu spuštěného na pozadí. Pro ten účel je nutné spustit na pozadí nejen ten příkaz, o který nám jde, ale po něm ještě uložení návratové hodnoty do souboru. Čili vlastně takový malý skriptík. Dvojici příkazů proto uzavřeme do kulatých závorek, čímž bash poprosíme o spuštění kombinace procesů v samostatném shellu. Ampersand se postará o spuštění na pozadí. (Složené závorky nepouští subshell, nelze je tedy použít pro spuštění na pozadí.)

$ (tar xzf rozbalit.tgz ; echo $? > exit_code_rozbaleni ) &

Až tar archiv rozbalí, a to může být kdykoli později, vznikne najednou soubor exit_code_rozbaleni obsahující návratovou hodnotu taru. Kontrolní otázka: proč jsme použili středník a ne &&?

A co návratová hodnota roury?

Než ukončíme dnešní díl, musím zmínit návratovou hodnotu řady programů spojených rourou. Standardně bash sleduje jen hodnotu posledního z programů, čili např. když soubor.txt.gz nejde rozbalit, „počítání řádek“ přesto uspěje:

$ zcat soubor.txt.gz | wc -l || echo "Problém s počítáním řádek"
$ # o problému s nalezením souboru soubor.txt.gz se skript nedozví

V bashi máte možnost buď studovat speciální proměnnou ${PIPESTATUS[0...n]} (vlastně je to pole, ale o tom zas někdy jindy), nebo nastavit volbu bashe, aby zachovával špatné zprávy:

$ set -o pipefail
$ zcat soubor.txt.gz | wc -l || echo "Nastal problém"
$ # dozvíte se o všech problémech, nalezení souboru i počítání řádek
Našli jste v článku chybu?
120na80.cz: Jsou opalovací krémy pro děti jiné?

Jsou opalovací krémy pro děti jiné?

120na80.cz: Běžecká lékárnička: jak si poradit?

Běžecká lékárnička: jak si poradit?

DigiZone.cz: Skylink přidává kanály už teď

Skylink přidává kanály už teď

DigiZone.cz: Mobilní aplikace pro DVTV je tady

Mobilní aplikace pro DVTV je tady

DigiZone.cz: Náhrada za nevrácená zařízení?

Náhrada za nevrácená zařízení?

Vitalia.cz: Máte chutě? Nejezděte do světa, ale do Dobřichovic

Máte chutě? Nejezděte do světa, ale do Dobřichovic

DigiZone.cz: Markíza: tady je předběžné opatření

Markíza: tady je předběžné opatření

DigiZone.cz: ČTÚ květen: rušení TV vysílání narůstá

ČTÚ květen: rušení TV vysílání narůstá

Podnikatel.cz: Oblíbené Babišovo reverse charge. Potopilo je?

Oblíbené Babišovo reverse charge. Potopilo je?

Lupa.cz: Vydavatelé jsou v háji, ale neumí si to připustit

Vydavatelé jsou v háji, ale neumí si to připustit

Vitalia.cz: 5 porcí ovoce a zeleniny: no ale jak na to?

5 porcí ovoce a zeleniny: no ale jak na to?

Podnikatel.cz: Jeho dřevěné hodinky chtějí na všech kontinentech

Jeho dřevěné hodinky chtějí na všech kontinentech

DigiZone.cz: Skylink: Nova Sport volně

Skylink: Nova Sport volně

Lupa.cz: Na základně u Dobříše se rozjel 3D tisk z kovu

Na základně u Dobříše se rozjel 3D tisk z kovu

Podnikatel.cz: Eseróčko vs. živnost. Co vyhrává?

Eseróčko vs. živnost. Co vyhrává?

Měšec.cz: Od kdy musí studenti platit pojistné?

Od kdy musí studenti platit pojistné?

Vitalia.cz: Další Míša má Klasu

Další Míša má Klasu

Měšec.cz: Ceny PHM v Evropě. Finty na úspory

Ceny PHM v Evropě. Finty na úspory

Podnikatel.cz: "Okurku" vyřeší slevové servery. Už jim věřte

"Okurku" vyřeší slevové servery. Už jim věřte

Root.cz: Quake slaví 20 let novou epizodou zdarma

Quake slaví 20 let novou epizodou zdarma