Menu

Druhé cvičení (1. 3.)

Standardní vstup, výstup a chybový výstup

Standardní vstup a výstup, resp. chybový výstup jsou vstupní a výstupní kanály, kterými proces komunikuje se svým prostředím (s shellem). Standardní vstup stdin je obvykle přijímán z klávesnice, standardní výstup stdout a standardní chybový výstup stderr jsou vypisovány na obrazovku. stderr slouží především pro výpis chybových hlášení.

Každý vstup, resp. výstup má přiřazeno číslo. Toto číslo souvisí se souborovým deskriptorem v jazyce C.

0 – stdin
1 – stdout
2 – stderr

Přesměrování standardního vstupu a výstupů nám umožňuje odpojit je od klávesnice a obrazovky a místo toho číst vstup, resp. vypisovat výstup jinam – do souboru, na standardní vstup jiného příkazu apod. Díky přesměrování spolu mohou jednotlivé programy komunikovat. Jedná se o naprosto zásadní techniku – jsou-li jednotlivé příkazy základními kameny, které tvoří budovu programování v shellu, pak přesměrování je maltou, která je drží pohromadě.

Přesměrování stdin

Standardní vstup lze načíst ze souboru místo z klávesnice:

příkaz < soubor
wc -l < /etc/passwd   ... vypíše počet řádek souboru /etc/passwd
wc -l 0< /etc/passwd  ... to samé, ale s explicitním zmíněním čísla 
                          deskriptoru; u stdin se v praxi téměř nepoužívá

Jaký je rozdíl mezi voláním wc -l < /etc/passwd a wc -l /etc/passwd? V druhém případě vypíše wc kromě počtu řádek i název souboru. V prvním případě ale data přišla na standardní vstup – wc neví o tom, že pochází ze souboru /etc/passwd a ani ho to nezajímá. Vypíše tedy pouze počet řádek.

Přesměrování stdout

Standardní výstup můžeme mimo jiné přesměrovat do souboru. Přesměrování do souboru má dvě varianty – podle toho, zda chceme cílový soubor přepsat, nebo jen připsat data na jeho konec. V každém případě, pokud přesměrováváme výstup do souboru, který ještě neexituje, bude automaticky vytvořen.

date > datum.txt   ... zapíše do souboru datum.txt aktuální datum 
                       a čas; původní obsah se přepíše
date >> datum.txt  ... připíše na konec souboru datum.txt 
                       aktuální datum a čas

Oba předchozí způsoby můžeme opět zapsat i s explicitním zmíněním čísla deskriptoru. Ani u standardního výstupu číslo deskriptoru obvykle neuvádíme, je však dobré si uvědomit, co se za > skutečně skrývá:

date 1> datum.txt
date 1>> datum.txt

Přesměrování stderr

Standardní chybový výstup můžeme stejně jako stdout přesměrovat do souboru a stejně jako v případě stdout se můžeme rozhodnout, zda soubor přepsat, či připisovat na jeho konec. V tomto případě však už skutečně musíme číslo deskriptoru uvést – žádný zkrácený zápis neexistuje.

ls /blabla 2> chyby.log
ls /blabla 2>> chyby.log

Výstup však nemusíme přesměrovat jenom do souboru. Někdy může být zajímavé přesměrovat stderr na stdout či (zřídkakdy) naopak.

ls /bla > vystup.txt 2>&1   ... stderr je přesměrován na stdout 
                                a ten pak do souboru vystup.txt
ls /bla 2> error.txt 1>&2   ... stdout je přesměrován na stderr 
                                a ten pak do souboru error.txt

Roura

Roura je mechanismus, který vezme dva příkazy a přesměruje standardní výstup prvního z nich na standardní vstup druhého. Zapisuje se pomocí |.

ls -1 ~ | wc -l  ... spočítá počet souborů v domovském adresáři

Pokud chceme, můžeme rouru opsat pomocí obyčejných přesměrování a dočasného souboru. Ten pak ale po sobě musíme uklidit, takže roura je obvykle praktičtější (a rychlejší). Výhodou je, že si mezi těmito voláními můžete vypsat obsah souboru docasny_soubor a snáze tak pochopíte, proč roura dělá to co dělá. Předchozí příkaz by se tedy dal provést i takto:

ls -1 ~ > ~/docasny_soubor
wc -l < ~/docasny_soubor

Dalším způsobem, jak si poznamenat do souboru data, aniž bychom museli přerušovat rouru je příkaz tee.

Nové příkazy

cat  – vypíše zadané soubory
tac  – vypíše zadané soubory s přehozeným pořadím řádků
rev  – vypíše zadané soubory s převrácenými řádky
cut  – vypíše jen zadané sloupce ze souboru
sort – setřídí řádky souboru
uniq – vypíše opakující se řádky
tr   – nahradí výskyt zadaných znaků jinými znaky
join – spojí soubory podle společného sloupce
last – výpis posledních přihlášení (není v POSIXu)
who  – zobrazí přihlášené uživatele
ps   – vypíše procesy bežící na terminálu

getent passwd – vypíše seznam všech uživatelů v síti 
                (není v POSIXu; labová specialita)

Příklady

Zobrazit řešení
  1. Vypište obsah aktuálního adresáře (tj. názvy všech souborů) do souboru obsah.
    ls > obsah
    
  2. Vypište 13. až 17. řádek ze souboru /etc/passwd.
    head -n17 /etc/passwd | tail -n+13
    
  3. Spusťte příkaz wc, jehož standardní vstup přesměrujete na /etc/passwd a standardní výstup na ~/vystup-wc
    wc < /etc/passwd > ~/vystup-wc
    
  4. Vytvořte soubor adresare, který na prvním řádku obsahuje /tmp, na druhém řádku /etc a na třetím řádku /. Vyzkoušejte příkaz ls < adresare, zjistěte co dělá a proč.
    echo -e "/tmp\n/etc\n/" > adresare
    
    Příkaz vypíše obsah aktuálního adresáře, protože ls ignoruje svůj standardní vstup.
  5. Vypište (pouze) loginy a jména všech uživatelů v síti.
    getent passwd | cut -d: -f1,5
    
  6. Zjistěte kolik různých křestních jmen mají uživatelé sítě. Za křestní jména počítejte i loginy nepatřící konkrétní osobě, jako test, vyuka či lp.
    getent passwd | cut -d: -f5 | cut -d\  -f1 | sort -u | wc -l
    
  7. Vypište jména uživatelů sítě, která se vyskytují více než jednou (buď proto, že se více osob jmenuje stejně, nebo protože má jedna osoba více loginů).
    getent passwd | cut -d: -f5 | sort | uniq -d
    
  8. Vypište jména uživatelů sítě, která se vyskytují více než jednou, VELKÝMI PÍSMENY a setřízená podle příjmení.
    getent passwd | cut -d: -f5 | sort | uniq -d | sort -k2
    
    Pozn.: Ve skutečnosti třídíme podle druhého jména, přičemž příjmení může být i na nějakém dalším místě. Řešení třídící opravdu podle příjmení je se zatím probranými prostředky složitější, i když ne nemožné.
  9. Napište příkaz, který přijímá řádky zadané na standardním vstupu a vypisuje je na standardní výstup zašifrované Ceasarovou šifrou (tj. cyklicky posunuté v abecedě o dvě písmena dál). Vyzkoušejte že příkaz funguje a poté přesměrujte na standardní vstup tohoto příkazu soubor /etc/passwd.
    < /etc/passwd tr 'a-zA-Z' 'c-zabC-ZAB'
    

Příklady pro pokročilé

  1. Napište skript, který ze zadané webové stránky vyparsuje všechny odkazy a vypíše je na standardní výstup. Relativní odkazy přepište na absolutní.