Dopředu jedno varování: používáme syntax bashe (typicky /bin/bash
), jiné shelly, např. csh, mohou požadovat zápis jiný.
Vstup a výstup procesu
Terminologie úvodem: „program“ je spustitelný soubor, „proces“ je pak jedna konkrétní běžící instance nějakého programu. Často tenhle jemný rozdíl nehraje roli, ale když např. jeden program spustíte dvakrát, je jasné, že musíte začít mluvit o procesech.
Odkud odevšad získává unixový proces nějaká vstupní data (pomineme-li např. síťové připojení)?
- vlastní kód programu, co je „zadrátováno“
- standardní vstup (stdin)
- příkazová řádka, tj. název programu a parametry, které dostal
- proměnné prostředí (např.
$USER
obsahující vaše uživatelské jméno) - systém souborů (filesystem), kolik přístupová práva dovolí
Jednotlivé zdroje vstupů se liší svým charakterem:
- Příkazová řádka i proměnné prostředí jsou vhodné jen na relativně malá data, například nastavení parametrů, krátký seznam souborů ke zpracování a podobně.
- Proměnné prostředí se (na rozdíl od všeho ostatního) dědí i pro procesy, které původní proces sám spustí.
- Standardní vstup může být neomezený do délky, nelze však v něm číst na přeskáčku nebo se vracet. Toho lze dosáhnout jen tak, že si část vstupu načteme a zapamatujeme. Hloupé programy načítají celý vstup, i když to třeba vůbec najednou nepotřebují, a nedokážou tak zpracovat větší vstup, než se vejde do paměti. Navíc je standardní vstup jen jeden.
- Systém souborů dovoluje prakticky cokoli, ale program přirozeně potřebuje napovědět, se kterými soubory pracovat, aby nemusel prohledávat všechny disky.
A co všechno může proces vracet?
- standardní výstup (stdout),
- chybový výstup (stderr),
- návratovou hodnotu (exit code), tj. jedno celé číslo,
- a samozřejmě může nějak modifikovat systém souborů, například něco někam uložit.
Povšimněte si, že program nemůže „vracet“ proměnné prostředí, ty lze měnit jen pro potomky, nikoli pro předky. Řeč je o „rodokmenu“ procesů podle toho, kdo koho spustil.
K čemu výše uvedený přehled? Každý program je jiný, předpokládá vstup jinde a vrací výstup jinam (nebo nás může zajímat něco méně obvyklého). Je tedy potřeba pečlivě číst manuálové stránky programů, abychom program spustili správně.
Základní přesměrování a roury
Jako příklad uvažme tr
, program pro zaměňování písmenek. tr
však na rozdíl od například cat
zpracovává zásadně stdin, nikoli soubory uvedené na příkazové řádce. Vstup ze souboru je tedy nutné na stdin nějak přivést, a to přesně shell umožňuje. Zde je příklad použití tr
, který každé slovo textu uvede na samostatný řádek (záměnou mezer za znak konce řádku). Shell v zadaném příkazu najde pokyn pro přisměrování vstupu (<), otevře udaný soubor.txt, spustí tr
a přivede mu obsah souboru na standardní vstup:
$ tr ' ' '\n' < soubor.txt
Roura ( |
) v příkazové řádce značí, že dva procesy na sebe navazují: standardní výstup jednoho je rovnou předán na standardní vstup druhého. Pro zpracování komprimovaného vstupu v našem příkladu napřed necháme soubor.txt.gz
rozbalit (program gunzip
) a vypsat ( -c
; bez tohoto parametru gunzip rozbalí soubor „na místě“ a nic nevypíše), roura pak text souboru přivede na vstup tr
:
$ gunzip -c soubor.txt.gz | tr ' ' '\n'
Výstup z tr
často chceme nikoli vypsat, ale raději uložit:
$ ... | tr ' ' '\n' > vystup.txt
nebo opět rovnou komprimovat:
$ ... | tr ' ' '\n' | gzip > vystup.txt.gz
Přesměrování chybového výstupu
Nyní už možná chápete, proč Unix odlišuje standardní a chybový výstup. Cenná data obyčejně tečou z programu do programu standardním výstupem a není žádoucí do nich přimíchat občasné chybové hlášky nebo upozornění. Právě k tomu slouží chybový výstup. Neurčíte-li jinak, chybový výstup se bude vypisovat na konzoli. V bashi můžete ale pohodlně chybový výstup přesměrovat do samostatného souboru nebo do „černé díry“ /dev/null
(speciální zařízení, které vše rovnou zahazuje). Zde je příklad, který prohledá celý disk. Protože však k řadě adresářů nemáte přístup, chybový výstup zahltí vaši konzoli stížnostmi a šťastně nalezený soubor (uvedený na standardním výstupu) můžete snadno přehlédnout. Chybový výstup tedy přesměrujeme do černé díry:
$ find / -name hledany_soubor.txt 2> /dev/null
Chybový výstup můžete rourou rovnou předat i jinému programu (například grep
pro vyhledání právě té chybové hlášky, která vás zajímá), musíte ale napřed přesměrovat chybový výstup na standardní (roura navazuje zásadně na standardní výstup). Takto spojíte standardní a chybový výstup dohromady:
$ find / -name hledany_soubor.txt 2>&1 | less
A takto standardní výstup pošlete do souboru a chybový povedete rourou:
$ find / -name hledany_soubor.txt 2>&1 > nalezene_soubory.txt | less
Upozorňuji, že pořadí, v jakém přesměrování uvedete, je podstatné. Intuici odpovídá, když budete číst pokyny odzadu: stdin do souboru, stderr na stdin. opačné pořadí ( > soubor 2>&1
) pošle vše do souboru a nic do roury.
Přesměrování do chybového výstupu
Pro výpis dlouhé chybové hlášky (například z nějakého delšího skriptu, až je společně začneme programovat) se může hodit opak: přesměrovat standardní výstup na chybový:
$ cat dlouha_chybova_hlaska.txt >&2
Pozorní čtenáři si všimli, že stdout má „číslo“ 1 a stderr „číslo“ 2. Je tomu skutečně tak, vstupy a výstupy programu jsou číslovány, takzvané file deskriptory, ale to už opravdu přesahuje základy shellu.
Víc už se nám do tohoto dílu nevejde, příště nás čeká zase kousek teorie, tentokrát o mezerách v příkazové řádce a kapinku o proměnných prostředí.