Co jsou stdin, stdout a stderr v Linuxu?

stdin, stdout a stderr jsou tři datové toky vytvořené při spuštění příkazu Linuxu. Můžete je použít ke zjištění, zda jsou vaše skripty přenášeny nebo přesměrovány. Ukážeme vám jak.

Streamy spojují dva body

Jakmile se začnete učit o Linuxu a operačních systémech podobných Unixu, setkáte se s pojmy stdin, stdout a stederr. Tyto jsou tři standardní proudy které se vytvoří při spuštění příkazu Linuxu. V oblasti výpočetní techniky je tok něco, co může přenášet data. V případě těchto datových proudů jsou těmito daty text.

Datové toky, stejně jako vodní toky, mají dva konce. Mají zdroj a odtok. Kterýkoli příkaz Linuxu, který používáte, poskytuje jeden konec každého streamu. Druhý konec je určen shellem, který spustil příkaz. Tento konec bude připojen k oknu terminálu, připojen k potrubí nebo přesměrován na soubor nebo jiný příkaz podle příkazového řádku, který příkaz spustil.

Standardní streamy Linuxu

V Linuxu je standardním vstupním tokem stdin. Toto přijímá text jako svůj vstup. Textový výstup z příkazu do shellu je dodáván prostřednictvím proudu stdout (standardní výstup). Chybové zprávy z příkazu jsou odesílány prostřednictvím proudu stderr (standardní chyba).

Můžete tedy vidět, že existují dva výstupní proudy, stdout a stderr, a jeden vstupní proud, stdin. Protože chybová hlášení a normální výstup mají každý svůj vlastní kanál, který je přenáší do okna terminálu, lze je zpracovávat nezávisle na sobě.

S proudy se zachází jako se soubory

Se streamy se v Linuxu – jako téměř se vším ostatním – zachází, jako by to byly soubory. Můžete číst text ze souboru a můžete jej zapisovat do souboru. Obě tyto akce zahrnují proud dat. Koncepce manipulace s proudem dat jako se souborem tedy není tak složitá.

Každému souboru přidruženému k procesu je přiděleno jedinečné číslo pro jeho identifikaci. Toto je známé jako deskriptor souboru. Kdykoli je vyžadováno provedení akce se souborem, deskriptor souboru slouží k identifikaci souboru.

Tyto hodnoty se vždy používají pro stdin, stdout a stderr:

0: stdin
1: stdout
2: stderr

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

Abychom někomu usnadnili úvod do předmětu, běžnou technikou je vyučovat zjednodušenou verzi tématu. Například u gramatiky je nám řečeno, že pravidlo je „já před E, kromě po C“. Ale ve skutečnosti tam je více výjimek z tohoto pravidla než jsou případy, které se tomu podřizují.

V podobném duchu, když mluvíme o stdin, stdout a stderr, je vhodné vyslovit přijatý axiom, že proces ani neví, ani se nestará, kde jsou jeho tři standardní toky ukončeny. Má se proces starat o to, zda jeho výstup jde do terminálu nebo je přesměrován do souboru? Dokáže dokonce zjistit, zda jeho vstup přichází z klávesnice nebo je do něj přenášen z jiného procesu?

Proces ve skutečnosti ví – nebo alespoň může zjistit, pokud se rozhodne zkontrolovat – a může podle toho změnit své chování, pokud se autor softwaru rozhodl tuto funkci přidat.

Tuto změnu chování vidíme velmi snadno. Zkuste tyto dva příkazy:

ls

ls | cat

Příkaz ls se chová odlišně, pokud je jeho výstup (stdout) přenášen do jiného příkazu. Je to ls, který přepíná na výstup jednoho sloupce, nejedná se o konverzi prováděnou cat. A ls dělá to samé, pokud je jeho výstup přesměrován:

ls > capture.txt

ls > capture.txt v okně terminálu” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<pre>cat capture.txt</pre>
<p><img loading=

Přesměrování stdout a stderr

Výhoda chybových zpráv doručovaných vyhrazeným streamem. To znamená, že můžeme přesměrovat výstup příkazu (stdout) do souboru a stále vidět chybové zprávy (stderr) v okně terminálu. V případě potřeby můžete na chyby reagovat, jakmile se vyskytnou. Také to zabrání tomu, aby chybové zprávy kontaminovaly soubor, do kterého byl stdout přesměrován.

Zadejte následující text do editoru a uložte jej do souboru s názvem error.sh.

#!/bin/bash

echo "About to try to access a file that doesn't exist"
cat bad-filename.txt

Udělejte skript spustitelný pomocí tohoto příkazu:

chmod +x error.sh

První řádek skriptu odešle text do okna terminálu prostřednictvím proudu stdout. Druhý řádek se pokusí získat přístup k neexistujícímu souboru. Tím se vygeneruje chybová zpráva doručená prostřednictvím stderr.

Spusťte skript pomocí tohoto příkazu:

./error.sh

Vidíme, že oba výstupní proudy, stdout a stderr, byly zobrazeny v oknech terminálu.

Zkusme přesměrovat výstup do souboru:

./error.sh > capture.txt

./error.sh > capture.txt v okně terminálu” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Chybová zpráva doručená přes stderr je stále odesílána do okna terminálu.  Můžeme zkontrolovat obsah souboru a zjistit, zda výstup stdout šel do souboru.</p>
<pre>cat capture.txt</pre>
<p><img loading=

Výstup z stdin byl přesměrován do souboru podle očekávání.

Symbol > přesměrování standardně funguje se stdout. K označení standardního výstupního proudu, který chcete přesměrovat, můžete použít jeden z číselných deskriptorů souborů.

Chcete-li explicitně přesměrovat stdout, použijte tuto instrukci pro přesměrování:

1>

Chcete-li explicitně přesměrovat stderr, použijte tuto instrukci pro přesměrování:

2>

Zkusme náš test znovu a tentokrát použijeme 2>:

./error.sh 2> capture.txt

./error.sh 2> capture.txt v okně terminálu” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Chybová zpráva je přesměrována a zpráva stdout echo je odeslána do okna terminálu:</p>
<p ><img class=

Zpráva stderr je podle očekávání v souboru capture.txt.

Přesměrování stdout i stderr

Jistě, pokud můžeme přesměrovat buď stdout nebo stderr do souboru nezávisle na sobě, měli bychom být schopni přesměrovat je oba současně, do dvou různých souborů?

Ano, můžeme. Tento příkaz přesměruje stdout do souboru s názvem capture.txt a stderr do souboru s názvem error.txt.

./error.sh 1> capture.txt 2> error.txt

./error.sh 1> capture.txt 2> error.txt v okně terminálu” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Protože oba proudy výstupu – standardní výstup i standardní chyba – jsou přesměrovány do souborů, není v něm žádný viditelný výstup okno terminálu.  Jsme vráceni do příkazového řádku, jako by se nic nestalo.</p>
<p><img loading=

Pojďme zkontrolovat obsah každého souboru:

cat capture.txt
cat error.txt

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

To je úhledné, každý ze standardních výstupních toků jde do vlastního vyhrazeného souboru. Jedinou další kombinací, kterou můžeme udělat, je odeslat stdout i stderr do stejného souboru.

Toho můžeme dosáhnout pomocí následujícího příkazu:

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

Pojďme to rozebrat.

./error.sh: Spustí soubor skriptu error.sh.
> capture.txt: Přesměruje stdout stream do souboru capture.txt. > je zkratka pro 1>.
2>&1: Používá instrukci přesměrování &>. Tato instrukce vám umožňuje říct shellu, aby se jeden proud dostal do stejného cíle jako jiný proud. V tomto případě říkáme „přesměrovat stream 2, stderr, do stejného cíle, na který je přesměrován stream 1, stdout.“

./error.sh > capture.txt 2&>1 v okně terminálu” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Neexistuje žádný viditelný výstup.  To je povzbudivé.</p>
<p><img loading=

Zkontrolujeme soubor capture.txt a uvidíme, co je v něm.

cat capture.txt

Proudy stdout i stderr byly přesměrovány do jediného cílového souboru.

Chcete-li mít výstup streamu přesměrován a tiše zahozen, nasměrujte výstup do /dev/null.

Detekce přesměrování v rámci skriptu

Diskutovali jsme o tom, jak může příkaz zjistit, zda je některý z proudů přesměrován, a podle toho se může rozhodnout změnit své chování. Můžeme toho dosáhnout v našich vlastních skriptech? Ano, můžeme. A je to velmi snadná technika na pochopení a použití.

Zadejte následující text do editoru a uložte jej jako input.sh.

#!/bin/bash

if [ -t 0 ]; then

  echo stdin coming from keyboard
 
else

  echo stdin coming from a pipe or a file
 
fi

K tomu, aby byl spustitelný, použijte následující příkaz:

chmod +x input.sh

Chytrá část je test v hranatých závorkách. Volba -t (terminál) vrátí hodnotu true (0), pokud je soubor přidružen k deskriptoru souboru končí v okně terminálu. Jako argument testu jsme použili deskriptor souboru 0, který představuje stdin.

Pokud je stdin připojen k oknu terminálu, test se ukáže jako pravdivý. Pokud je stdin připojen k souboru nebo kanálu, test se nezdaří.

Ke generování vstupu do skriptu můžeme použít jakýkoli vhodný textový soubor. Zde používáme soubor s názvem dummy.txt.

./input.sh 

The output shows that the script recognizes that the input isn’t coming from a keyboard, it is coming from a file. If you chose to, you could vary your script’s behavior accordingly.

That was with a file redirection, let’s try it with a pipe.

cat dummy.txt | ./input.sh

Skript rozpozná, že je do něj přenášen jeho vstup. Přesněji řečeno, znovu rozpozná, že proud stdin není připojen k oknu terminálu.

Spusťte skript bez potrubí ani přesměrování.

./input.sh

Tok stdin je připojen k oknu terminálu a skript to odpovídajícím způsobem hlásí.

Abychom totéž zkontrolovali s výstupním proudem, potřebujeme nový skript. Zadejte následující text do editoru a uložte jej jako output.sh.

#!/bin/bash

if [ -t 1 ]; then

echo stdout is going to the terminal window
 
else

echo stdout is being redirected or piped
 
fi

K tomu, aby byl spustitelný, použijte následující příkaz:

chmod +x input.sh

Jediná podstatná změna tohoto skriptu je v testu v hranatých závorkách. Pro reprezentaci deskriptoru souboru pro stdout používáme číslici 1.

Pojďme to zkusit. Výstup zavedeme přes kočku.

./output | cat

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

Skript můžeme také otestovat přesměrováním výstupu do souboru.

./output.sh > capture.txt

Neexistuje žádný výstup do okna terminálu, jsme tiše vráceni do příkazového řádku. Jak bychom očekávali.

Můžeme se podívat do souboru capture.txt, abychom viděli, co bylo zachyceno. Použijte k tomu následující příkaz.

cat capture.sh

Jednoduchý test v našem skriptu opět detekuje, že stdout stream není odesílán přímo do okna terminálu.

Pokud skript spustíme bez jakýchkoli kanálů nebo přesměrování, měl by detekovat, že stdout je doručován přímo do okna terminálu.

./output.sh

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

Proudy Vědomí

Vědět, jak zjistit, zda jsou vaše skripty připojeny k oknu terminálu, potrubí nebo jsou přesměrovány, vám umožňuje odpovídajícím způsobem upravit jejich chování.

Protokolování a diagnostický výstup může být více či méně podrobný v závislosti na tom, zda jde na obrazovku nebo do souboru. Chybová hlášení mohou být protokolována do jiného souboru, než je běžný výstup programu.

Jak už to tak bývá, více znalostí přináší více možností.