Hlavní navigace

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

3. 3. 2008
Doba čtení: 4 minuty

Sdílet

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 &&?

CS24_early

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

Byl pro vás článek přínosný?

Autor článku