4.3.
Bash / Vstup, výstup a přesměrování
1. Úvod
Tato kapitola pokrývá nástroje interpretu Bash k ovládání vstupů a výstupů spouštěných příkazů i vstupů a výstupů samotného interpretu. Rovněž pokrývá nástroje ke čtení textových řetězců ze souborů, terminálu či výstupu spoustěného programu a nástroje k zápisu textových řetězců do souboru, na terminál, do souboru nebo na vstup spouštěného programu.
Interpret Bash je vyvíjen v rámci projektu GNU.
2. Definice
- Deskriptor je (nejen v Bashi, ale pro každý jednotlivý proces v systému) číslovaný vstup nebo výstup. Základní deskriptory jsou „standardní vstup“ (číslo 0, „stdin“, budu zkracovat jako „s.vstup“), „standardní výstup“ (číslo 1, „stdout“, budu zkracovat jako „s.výstup“) a „standardní chybový výstup“ (číslo 2, „stderr“, budu zkracovat jako „s.ch. výstup“). Deskriptory 3 až 9 jsou určeny pro libovolné použití, deskriptory 10 až 255 pro vnitřní použití interpretem.
- Vstup je deskriptor, ze kterého může proces číst data.
- Výstup je deskriptor, do kterého může proces zapisovat data.
- Roura („pipeline“, v některých příručkách také nazývaná „kolona“) je spojení dvou nebo více jednoduchých příkazů operátorem „|“. Bash pak tyto příkazy spustí paralelně a připojí standardní výstup příkazu nalevo od | na standardní vstup příkazu napravo od |. Díky tomu pak data „protékají“ přímo z jednoho procesu do druhého. Návratovým kódem roury je ve výchozím nastavení návratový kód posledního uvedeného jednoduchého příkazu.
- Přesměrování deskriptorů (či jen přesměrování) je úkon, při kterém Bash něco provede s deskriptory vznikajícího procesu (nebo svými vlastními). Přesměrování se provádí jedno po druhém zleva doprava, jak jsou zadána na příkazové řádce.
Užitečná poznámka ke spouštění příkazů: kdykoliv z Bashe spustíte jakýkoliv nový proces, ten nejprve zdědí všechny deskriptory od interpretu (ne jen ty tři základní), pak pro něj Bash provede přesměrování deskriptorů specifikovaná na příkazovém řádku (včetně propojení procesů rour) a pak se teprve pokusí program spustit. To znamená, že vedlejší účinky přesměrování (např. vytvoření souborů) se projeví i v případě, že se Bashi program spustit nepodaří (např. proto, že program takového názvu neexistuje).
3. Zaklínadla
3/1 Roura
3/2 Přesměrování vstupu (čtení odněkud)
3/3 Přesměrování výstupu (zápis někam)
3/4 Některá obvyklá přesměrování
3/5 Pojmenované deskriptory
3/6 Ostatní aplikace přesměrování
4. Zaklínadla: Čtení a zápis z Bashe
4/1 Zápis (výstup)
4/2 Čtení (vstup)
Před použitím zaklínadel z této podsekce si prosím přečtěte podsekci „Jak funguje příkaz read“!
4/3 Interakce s uživatelem (jen při čtení z terminálu)
4/4 Je deskriptor připojený na terminál?
4/5 Nastavení Bashe související s deskriptory a rourami
5. Další poznámky
5/1 Použití přesměrování deskriptorů
Přesměrování deskriptorů se obvykle uvádějí na konec příkazu, za poslední parametr; např. takto:
Ale Bash je dovoluje uvést kamkoliv, dokonce i před příkaz nebo mezi parametry, např.:
Přesměrování je možno aplikovat i na celý blok příkazů, na cykly (např. „while“) apod.
Zvláštní pozornost je potřeba věnovat použití „bloku řádků“ jako vstupu; definice ukončovače se totiž uvádí za značku <<, ale číst vstup začne Bash až od následující řádky, což umožňuje tento druh vstupu skombinovat např. s rourou:
5/2 Jak funguje příkaz read
- Před zahájením čtení „read“ do všech uvedených proměnných přiřadí prázdný řetězec. (Jde-li o čtení do pole, pole se vyprázdní.)
- Čte ze zadaného deskriptoru (výchozí je s.vstup, tedy 0) znak po znaku a přidává je na konec první zadané proměnné (resp. prvního prvku pole).
- Když na vstupu narazí na oddělovač (kterýkoliv znak z hodnoty proměnné IFS), přeskočí na další proměnnou/další prvek pole, s výjimkou případu, kdy se oddělovač nachází bezprostředně před ukončovačem záznamu; v takovém případu je oddělovač ignorován. Do poslední zadané proměnné se pak načte celý zbytek záznamu (tam už je hodnota proměnné IFS ignorována).
- Když „read“ na vstupu narazí na ukončovač záznamu (výchozí je „\n“), úspěšně tím skončí čtení (tzn. návratový kód 0).
- Když „read“ narazí na konec vstupu (nebo hardwarovou chybu), skončí čtení s návratovým kódem 1. Již načtené znaky budou v proměnných ponechány.
Postřehy:
- Ne-ASCII znaky lze použít v proměnné IFS (tzn. jako oddělovače záznamů), ale ne v parametru „-d“ (tzn. nemohou sloužit jako ukončovače záznamů).
- Nulový bajt lze použít jako ukončovač záznamu (tvar parametru je pak „-d ""“), ale ne jako oddělovač záznamů (nelze ho uložit do proměnné IFS).
- V případě, že „read“ narazí na konec vstupu, skončí s návratovým kódem 1, ale již přečtené znaky ponechá v příslušných proměnných.
- Nastavíte-li časový limit, v případě jeho vypršení skončí „read“ s kódem 142 a načítané proměnné budou vyprázdněny.
5/3 Jak funguje příkaz printf
Ve formátovacím řetězci jsou interpretovány formátovací značky (uvozené znakem „%“) a lomítkové sekvence (začínající zpětným lomítkem „\“), oba případy lze odzvláštnit zdvojením (tzn. „%%“ se interpretuje jako obyčejný znak % a „\\“ jako obyčejné zpětné lomítko). Netriviální formátovací řetězce doporučuji uzavřít do apostrofů, aby nedocházelo ke konfliktům se zvláštním významem některých znaků na příkazovém řádku.
Algoritmus příkazu printf:
- 1. Vzít formátovací řetězec a nahradit v něm všechny lomítkové sekvence odpovídajícími znaky či řetězci.
- 2. Projít všechny formátovací značky ve formátovacím řetězci zleva doprava; pro každou načíst jeden parametr z parametrů za formátovacím řetězcem (pokud chybí, použít místo něj prázdný řetězec), zformátovat podle značky a dosadit místo ní.
- 3. Výsledný řetězec vypsat na standardní výstup.
- 4. Pokud zbyl alespoň jeden parametr, vzít řetězec, který byl výstupem kroku 1 a jít na krok 2 se zbylými parametry.
V případě použití parametru „-v“ se řetězce místo vypsání ukládají do zadané proměnné a nesmí obsahovat nulový bajt (jinak může být chování nepředvídatelné).
6. Instalace na Ubuntu
Bash a všechny příkazy použité v této kapitole jsou základními součástmi Ubuntu přítomnými i v minimální instalaci.
7. Tipy a zkušenosti
- Při práci s deskriptory je třeba mít na paměti, že „pozice“ čtení či zápisu není vlastností samotného deskriptoru. Otevřením souboru přesměrováním deskriptoru vznikne nová „čtecí pozice“ i v případě, že stejný soubor je již otevřen přes jiný deskriptor; pokud ale stávající deskriptor zduplikujete nebo ho zdědí nový proces, nová pozice nevznikne a čtení z obou deskriptorů bude posouvat tutéž čtecí pozici (existující pravděpodobně někde v jádře).
7/1 Časté chyby s rourou
Pozor na implicitní vznik podprostředí v některých situacích! Bash automaticky obklopí podprostředím každý jednoduchý příkaz roury a také i jednoduchý příkaz spouštěný na pozadí nebo v operátoru „$()“. To znamená, že např. tento blok kódu vypíše „19“, protože přiřazení z konstrukce „:=“ zůstalo izolované v podprostředí:
Nejčastěji se tato chyba vyskytuje ve formě pokusu o použití příkazu „read“ s rourou:
V uvedeném příkladu zůstane hodnota „a“ nedefinovaná, protože Bash uzavře příkaz „read“ do samostatného podprostředí.
7/2 Problémy s nulovým bajtem
Bash obecně neumí pracovat s nulovým bajtem „\0“; umí ho však vygenerovat (příkazem printf) a přečíst jako ukončovač záznamu (příkazy read a readarray) a rovněž může být předáván rourou z příkazu do příkazu (což probíhá mimo Bash, ten rouru jen vytváří). Celkově je třeba při jakémkoliv pokusu o prácí s nulovým bajtem nástroji Bashe dát velký pozor, zejména ho nelze uložit do proměnných ani použít uvnitř textu parametru jakéhokoliv příkazu.
8. Další zdroje informací
- TL;DR: printf (anglicky)
- TL;DR: read (anglicky)
9. Zákulisí kapitoly
V této verzi kapitoly chybí:
- koprocesy
- více „dalších zdrojů informací“
- podrobnější výklad o „printf“
Tato kapitola záměrně nepokrývá:
- nic