Co jsou stdin, stdout a stderr v Linuxu?

Při práci s Linuxem se často setkáte s pojmy jako stdin, stdout a stderr. Jedná se o tři základní datové toky, které se aktivují při spuštění každého příkazu v Linuxu. Tyto datové toky slouží k předávání textových dat a umožňují sofistikované řízení vstupu a výstupu. Dnes se podíváme na to, jak tyto toky fungují a jak je lze využít.

Spojení mezi dvěma body: Datové toky

Standardní toky (stdin, stdout a stderr) jsou stěžejní pro interakci s příkazy v operačních systémech podobných Unixu. Tyto tři standardní datové toky vznikají při každém spuštění příkazu. V kontextu výpočetní techniky si můžeme tok představit jako prostředek pro přenos dat, v tomto případě textových. Podobně jako vodní tok, i datový tok má svůj zdroj a cíl. Každý příkaz v Linuxu poskytuje jeden konec každého z těchto toků. Druhý konec je určen shellem, který příkaz inicializoval. Tento konec může směřovat do terminálu, do roury (pipeline), do souboru nebo k jinému příkazu, dle specifikace v příkazovém řádku.

Standardní toky v Linuxu: Vstup, výstup a chyby

V Linuxu je standardní vstupní tok označován jako stdin. Tento tok přijímá textový vstup. Textový výstup generovaný příkazem se posílá do stdout (standardní výstup), zatímco chybové hlášení jsou směrovány do stderr (standardní chybový výstup). Díky tomu, že výstupní toky stdout a stderr mají vlastní kanály, lze s běžným výstupem a chybovými hláškami pracovat odděleně.

Datové toky jako soubory

V Linuxu se se všemi toky manipuluje, jako by to byly soubory. To znamená, že můžeme číst data z toku (jako ze souboru) a také do toku zapisovat. Pro identifikaci každého souboru (a tedy i toku) je používán tzv. deskriptor souboru, což je unikátní číslo přiřazené danému procesu. Pro stdin, stdout a stderr jsou standardně vyhrazeny deskriptory 0, 1 a 2.

0: stdin
1: stdout
2: stderr

Reakce na roury a přesměrování

Často se říká, že procesy v Linuxu neřeší, kam směřují jejich standardní toky. Tvrdí se, že proces by neměl vědět, zda jeho výstup jde na terminál nebo je přesměrován do souboru. Nicméně, opak je pravdou. Procesy mohou zjistit, kam směřují jejich standardní toky a mohou se na základě této informace chovat jinak. Tuto flexibilitu můžeme pozorovat v následujících příkladech.

Podívejme se na rozdíl v chování příkazu ls v následujících dvou případech:

ls
ls | cat

Všimněte si, že příkaz ls se chová odlišně, pokud je jeho výstup (stdout) odesílán do jiného příkazu. Příkaz ls automaticky přepne na výstup do jednoho sloupce. Stejný efekt se projeví, když je výstup příkazu ls přesměrován do souboru:

ls > vystup.txt

cat vystup.txt

Přesměrování stdout a stderr

Výhodou oddělených výstupních toků (stdout a stderr) je možnost přesměrování výstupu do souboru a současné sledování chybových hlášek na terminálu. To umožňuje okamžitou reakci na chyby a zamezí jejich mísení se standardním výstupem.

Vytvořte následující skript v editoru a uložte ho jako error.sh:

#!/bin/bash
echo "Chystám se pokusit o přístup k neexistujícímu souboru"
cat neexistujici-soubor.txt

Nastavte skript jako spustitelný:

chmod +x error.sh

První řádek skriptu odešle text do stdout a druhý řádek se pokusí o přístup k neexistujícímu souboru, což vygeneruje chybovou zprávu přes stderr.

Spusťte skript:

./error.sh

Vidíme, že oba výstupní toky se zobrazí na terminálu.

Zkusíme přesměrovat standardní výstup do souboru:

./error.sh > vystup.txt

Chybová zpráva (stderr) se stále zobrazí na terminálu. Zkontrolujme obsah souboru:

cat vystup.txt

Standardní výstup se přesměroval do souboru, jak bylo očekáváno.

Symbol > standardně přesměrovává stdout. Můžeme také použít deskriptory souborů:

Pro explicitní přesměrování stdout se používá instrukce: 1>

Pro explicitní přesměrování stderr se používá instrukce: 2>

Zkusme náš test znovu, ale tentokrát přesměrujeme stderr:

./error.sh 2> vystup.txt

Chybová zpráva (stderr) je přesměrována do souboru a standardní výstup (stdout) se vypíše na terminál:

Chybová zpráva je v souboru vystup.txt.

Přesměrování stdout i stderr

Pokud můžeme přesměrovávat stdout a stderr odděleně, můžeme je i přesměrovat současně do různých souborů? Ano, můžeme. Následující příkaz přesměruje stdout do souboru vystup.txt a stderr do souboru chyby.txt:

./error.sh 1> vystup.txt 2> chyby.txt

V terminálu se nezobrazí žádný výstup, protože oba toky jsou přesměrovány do souborů.

Zkontrolujme obsah obou souborů:

cat vystup.txt
cat chyby.txt

Přesměrování stdout a stderr do stejného souboru

Poslední kombinací je přesměrování stdout i stderr do stejného souboru. Toho dosáhneme následujícím příkazem:

./error.sh > vystup.txt 2>&1

Pojďme si to rozebrat:

  • ./error.sh: Spustí skript error.sh.
  • > vystup.txt: Přesměruje stdout do souboru vystup.txt. Zkratka pro 1>.
  • 2>&1: Přesměruje stderr do stejného místa jako stdout.

V terminálu se nezobrazí žádný výstup.

Zkontrolujme obsah souboru vystup.txt:

cat vystup.txt

Oba toky (stdout a stderr) jsou přesměrovány do jediného cílového souboru.

Pro přesměrování výstupu do černé díry (tiché zahazování) můžete použít /dev/null.

Detekce přesměrování ve skriptu

Můžeme také detekovat přesměrování standardních toků přímo v našich skriptech? Ano, můžeme. Je to poměrně jednoduchá technika. Vytvořte nový skript s názvem input.sh:

#!/bin/bash
if [ -t 0 ]; then
  echo "stdin přichází z klávesnice"
else
  echo "stdin přichází z roury nebo souboru"
fi

Udělejte skript spustitelný:

chmod +x input.sh

Klíčem je test v hranatých závorkách. Volba -t (terminál) vrací true (0), pokud deskriptor souboru končí na terminálu. Jako argument testu jsme použili 0 (stdin).

Pokud je stdin připojen k terminálu, test je pravdivý. Jinak test selže.

Použijme k vygenerování vstupu do skriptu nějaký soubor. Zde použijeme soubor dummy.txt.

./input.sh < dummy.txt

Výstup ukazuje, že skript rozpozná, že vstup nepochází z klávesnice, ale ze souboru. Můžete upravit chování skriptu podle této informace.

Zkusme to s rourou:

cat dummy.txt | ./input.sh

Skript rozpozná, že vstup je přenášen rourou a není připojen k terminálu.

Spustíme skript bez roury a přesměrování:

./input.sh

Vstupní tok je připojen k terminálu a skript to nahlásí.

Pro otestování výstupního toku vytvoříme nový skript output.sh:

#!/bin/bash
if [ -t 1 ]; then
  echo "stdout směřuje do terminálu"
else
  echo "stdout je přesměrován nebo v rourě"
fi

Nastavíme skript jako spustitelný:

chmod +x input.sh

Jediná změna je, že v testu používáme deskriptor souboru 1 pro stdout.

Zkusme to. Pošleme výstup přes cat:

./output.sh | cat

Skript rozpozná, že výstup nesměřuje přímo do terminálu.

Otestujeme skript přesměrováním výstupu do souboru:

./output.sh > vystup.txt

V terminálu se žádný výstup nezobrazí.

Podívejme se do souboru vystup.txt:

cat vystup.sh

Test ve skriptu detekuje, že výstup nesměřuje přímo do terminálu.

Pokud skript spustíme bez přesměrování nebo roury, skript detekuje, že stdout směřuje do terminálu:

./output.sh

A to je přesně to, co vidíme.

Znalost toků

Znalost toho, jak detekovat, zda je skript připojen k terminálu, potrubí nebo je přesměrován, vám umožňuje přizpůsobit jeho chování. Protokolování a diagnostické výstupy mohou být více nebo méně podrobné, v závislosti na tom, zda výstup směřuje na obrazovku nebo do souboru. Chybové hlášky mohou být logovány do jiného souboru, než je standardní výstup programu. Větší znalosti přináší více možností.