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
120na80.cz: Vyzrajte na návaly a pocení v přechodu

Vyzrajte na návaly a pocení v přechodu

Podnikatel.cz: Když už je sexy, tak ať taky funguje

Když už je sexy, tak ať taky funguje

120na80.cz: 10 nej přípravků na holení

10 nej přípravků na holení

Vitalia.cz: Syndrom PC vidění: stačí dvě hodiny denně

Syndrom PC vidění: stačí dvě hodiny denně

Vitalia.cz: Vyřízli vám znaménko. Jak se z něj pozná rakovina?

Vyřízli vám znaménko. Jak se z něj pozná rakovina?

Vitalia.cz: Mražené ryby z Makra byly falšované

Mražené ryby z Makra byly falšované

Vitalia.cz: Tetanus v USA – i po odřeninách

Tetanus v USA – i po odřeninách

Vitalia.cz: 7 nemocí očí, které musíte léčit včas

7 nemocí očí, které musíte léčit včas

DigiZone.cz: Šlágr TV dostala pokutu 100 000 Kč

Šlágr TV dostala pokutu 100 000 Kč

Podnikatel.cz: Rošáda v živnostech. Týká se vás?

Rošáda v živnostech. Týká se vás?

Vitalia.cz: Muži kouří 24 cigaret denně, ženy o dost míň

Muži kouří 24 cigaret denně, ženy o dost míň

DigiZone.cz: Živí mrtví budou na AMC koncem srpna

Živí mrtví budou na AMC koncem srpna

Vitalia.cz: SÚKL: vakcíny jsou bezpečné a s autismem nesouvisí

SÚKL: vakcíny jsou bezpečné a s autismem nesouvisí

DigiZone.cz: Konec geoblokace online médií?

Konec geoblokace online médií?

Root.cz: Anonymous sejmuli Senat.cz kvůli cenzuře

Anonymous sejmuli Senat.cz kvůli cenzuře

120na80.cz: Zjistěte, zda je vaše klíště infikované

Zjistěte, zda je vaše klíště infikované

Lupa.cz: Zaplatíme ti, když ti seženeme práci

Zaplatíme ti, když ti seženeme práci

Podnikatel.cz: Šizený guláš na pultě. Jako Lidl to nedělejte

Šizený guláš na pultě. Jako Lidl to nedělejte

Vitalia.cz: Před, nebo po snídani? Kdy je lepší čistit si zuby

Před, nebo po snídani? Kdy je lepší čistit si zuby

Lupa.cz: Přenos hokeje padal kvůli útoku, tvrdí O2

Přenos hokeje padal kvůli útoku, tvrdí O2