Какво представляват stdin, stdout и stderr в Linux?

stdin, stdout и stderr са три потока от данни, създадени, когато стартирате команда на Linux. Можете да ги използвате, за да разберете дали вашите скриптове се предават или пренасочват. Ние ви показваме как.

Потоците се съединяват в две точки

Веднага щом започнете да научавате за Linux и Unix-подобни операционни системи, ще срещнете термините stdin, stdout и stederr. Това са три стандартни потока които се установяват при изпълнение на команда на Linux. В компютрите потокът е нещо, което може да прехвърля данни. В случай на тези потоци тези данни са текстови.

Потоците от данни, подобно на водните потоци, имат два края. Те имат източник и изход. Която и команда на Linux да използвате, осигурява единия край на всеки поток. Другият край се определя от обвивката, която стартира командата. Този край ще бъде свързан към прозореца на терминала, свързан към тръба или пренасочен към файл или друга команда, според командния ред, който стартира командата.

Стандартните потоци на Linux

В Linux stdin е стандартният входен поток. Това приема текст като вход. Извеждането на текст от командата към обвивката се доставя чрез потока stdout (стандартен изход). Съобщенията за грешки от командата се изпращат през потока stderr (стандартна грешка).

Така че можете да видите, че има два изходни потока, stdout и stderr, и един входен поток, stdin. Тъй като съобщенията за грешки и нормалният изход имат свой собствен канал за пренасянето им до прозореца на терминала, те могат да се обработват независимо един от друг.

Потоците се обработват като файлове

Потоците в Linux – както почти всичко останало – се третират като файлове. Можете да четете текст от файл и можете да пишете текст във файл. И двете действия включват поток от данни. Така че концепцията за обработка на поток от данни като файл не е толкова сложна.

На всеки файл, свързан с процес, се присвоява уникален номер, за да го идентифицира. Това е известно като файлов дескриптор. Всеки път, когато се изисква да се извърши действие върху файл, файловия дескриптор се използва за идентифициране на файла.

Тези стойности винаги се използват за stdin, stdout и stderr:

0: stdin
1: стандартен изход
2: stderr

Реагиране на тръби и пренасочвания

За да се улесни нечие въведение в даден предмет, често срещана техника е да се преподава опростена версия на темата. Например, с граматиката ни се казва, че правилото е „I преди E, освен след C“. Но всъщност там са повече изключения от това правило отколкото има случаи, които му се подчиняват.

  Как да инсталирате Inkscape на Linux

По подобен начин, когато говорим за stdin, stdout и stderr, е удобно да се изтъкне приетата аксиома, че процесът нито знае, нито се интересува къде са прекратени неговите три стандартни потока. Трябва ли даден процес да се интересува дали неговият изход отива към терминала или се пренасочва във файл? Може ли дори да разбере дали входът му идва от клавиатурата или се прехвърля в него от друг процес?

Всъщност процесът знае – или поне може да разбере, ако избере да провери – и може да промени поведението си съответно, ако авторът на софтуера реши да добави тази функционалност.

Можем да видим тази промяна в поведението много лесно. Опитайте тези две команди:

ls

ls | cat

Командата ls се държи по различен начин, ако нейният изход (stdout) се прехвърля в друга команда. Именно ls превключва към изход от една колона, това не е преобразуване, извършено от cat. И ls прави същото, ако неговият изход се пренасочва:

ls > capture.txt

ls > capture.txt в терминален прозорец” 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=

Пренасочване на stdout и stderr

Има предимство съобщенията за грешки да се доставят от специален поток. Това означава, че можем да пренасочим изхода на команда (stdout) към файл и все още да виждаме всички съобщения за грешка (stderr) в прозореца на терминала. Можете да реагирате на грешките, ако е необходимо, когато се появят. Той също така спира съобщенията за грешка да замърсят файла, към който е пренасочен stdout.

Въведете следния текст в редактор и го запишете във файл, наречен error.sh.

#!/bin/bash

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

Направете скрипта изпълним с тази команда:

chmod +x error.sh

Първият ред на скрипта отразява текста в прозореца на терминала чрез потока stdout. Вторият ред се опитва да получи достъп до файл, който не съществува. Това ще генерира съобщение за грешка, което се доставя чрез stderr.

Стартирайте скрипта с тази команда:

./error.sh

Можем да видим, че и двата изходни потока, stdout и stderr, са показани в прозорците на терминала.

Нека се опитаме да пренасочим изхода към файл:

./error.sh > capture.txt

./error.sh > capture.txt в прозорец на терминала” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Съобщението за грешка, което се доставя чрез stderr, все още се изпраща до прозореца на терминала.  Можем да проверим съдържанието на файла, за да видим дали изходът на stdout е отишъл във файла.</p>
<pre>cat capture.txt</pre>
<p><img loading=

Резултатът от stdin беше пренасочен към файла, както се очакваше.

  Как автоматично да преименувате медийни файлове на Linux с FileBot

Символът за пренасочване работи със stdout по подразбиране. Можете да използвате един от цифровите файлови дескриптори, за да посочите кой стандартен изходен поток искате да пренасочите.

За да пренасочите изрично stdout, използвайте тази инструкция за пренасочване:

1>

За да пренасочите изрично stderr, използвайте тази инструкция за пренасочване:

2>

Нека опитаме отново нашия тест и този път ще използваме 2>:

./error.sh 2> capture.txt

./error.sh 2> capture.txt в терминален прозорец” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Съобщението за грешка се пренасочва и ехо съобщението на stdout се изпраща до прозореца на терминала:</p>
<p ><img клас=

Съобщението stderr е в capture.txt, както се очаква.

Пренасочване както на stdout, така и на stderr

Разбира се, ако можем да пренасочим или stdout, или stderr към файл независимо един от друг, би трябвало да можем да ги пренасочим едновременно към два различни файла?

Да, можем. Тази команда ще насочи stdout към файл, наречен capture.txt, и stderr към файл, наречен error.txt.

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

./error.sh 1> capture.txt 2> error.txt в терминален прозорец” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Тъй като и двата потока от изход – стандартен изход и стандартна грешка – са пренасочени към файлове, няма видим изход в прозореца на терминала.  Връщаме се към подканата на командния ред, сякаш нищо не се е случило.</p>
<p><img src==

Нека проверим съдържанието на всеки файл:

cat capture.txt
cat error.txt

Пренасочване на stdout и stderr към същия файл

Това е страхотно, имаме всеки от стандартните изходни потоци, отиващи в собствен специален файл. Единствената друга комбинация, която можем да направим, е да изпратим както stdout, така и stderr към един и същ файл.

Можем да постигнем това със следната команда:

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

Нека разбием това.

./error.sh: Стартира скриптния файл error.sh.
> capture.txt: Пренасочва потока stdout към файла capture.txt. > е съкращение за 1>.
2>&1: Това използва инструкцията за пренасочване &>. Тази инструкция ви позволява да кажете на обвивката да накара един поток да стигне до същата дестинация като друг поток. В този случай казваме „пренасочване на поток 2, stderr, към същата дестинация, към която се пренасочва поток 1, stdout.“

./error.sh > capture.txt 2&>1 в терминален прозорец” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Няма видим изход.  Това е обнадеждаващо.</p>
<p><img loading=

Нека проверим файла capture.txt и да видим какво има в него.

cat capture.txt

И двата потока stdout и stderr са пренасочени към един файл дестинация.

За да бъде пренасочен изходът на поток и безшумно изхвърлен, насочете изхода към /dev/null.

Откриване на пренасочване в рамките на скрипт

Обсъдихме как една команда може да открие дали някой от потоците се пренасочва и може да избере съответно да промени поведението си. Можем ли да постигнем това в нашите собствени скриптове? Да, можем. И това е много лесна за разбиране и използване техника.

  Как да получите отдалечен достъп до вашия Linux компютър с NoMachine

Въведете следния текст в редактор и го запазете като input.sh.

#!/bin/bash

if [ -t 0 ]; then

  echo stdin coming from keyboard
 
else

  echo stdin coming from a pipe or a file
 
fi

Използвайте следната команда, за да го направите изпълним:

chmod +x input.sh

Умната част е тест в квадратни скоби. Опцията -t (терминал) връща true (0), ако файлът е свързан с файловия дескриптор завършва в прозореца на терминала. Използвахме файловия дескриптор 0 като аргумент на теста, който представлява stdin.

Ако stdin е свързан към прозорец на терминала, тестът ще се окаже верен. Ако stdin е свързан към файл или канал, тестът ще бъде неуспешен.

Можем да използваме всеки удобен текстов файл, за да генерираме вход към скрипта. Тук използваме такъв, наречен 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

Скриптът разпознава, че неговият вход се прехвърля в него. Или по-точно, той разпознава още веднъж, че stdin потокът не е свързан с терминален прозорец.

Нека стартираме скрипта без тръби или пренасочвания.

./input.sh

Потокът stdin е свързан към прозореца на терминала и скриптът отчита това съответно.

За да проверим същото нещо с изходния поток, имаме нужда от нов скрипт. Въведете следното в редактор и го запазете като 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

Използвайте следната команда, за да го направите изпълним:

chmod +x input.sh

Единствената значителна промяна в този скрипт е в теста в квадратните скоби. Използваме цифрата 1, за да представим файловия дескриптор за stdout.

Нека го изпробваме. Ще прехвърлим изхода през cat.

./output | cat

Скриптът разпознава, че неговият изход не отива директно в прозорец на терминала.

Можем също да тестваме скрипта, като пренасочим изхода към файл.

./output.sh > capture.txt

Няма изход към прозореца на терминала, ние се връщаме безшумно към командния ред. Както бихме очаквали.

Можем да погледнем във файла capture.txt, за да видим какво е заснето. Използвайте следната команда, за да направите това.

cat capture.sh

Отново, простият тест в нашия скрипт открива, че потокът stdout не се изпраща директно към прозорец на терминала.

Ако стартираме скрипта без никакви канали или пренасочвания, той трябва да открие, че stdout се доставя директно в прозореца на терминала.

./output.sh

И точно това виждаме.

Потоци на съзнанието

Знаейки как да разберете дали вашите скриптове са свързани към прозореца на терминала, или тръба, или са пренасочени, ви позволява да коригирате поведението им съответно.

Регистрирането и диагностичният изход могат да бъдат повече или по-малко подробни, в зависимост от това дали отиват на екрана или към файл. Съобщенията за грешки могат да се записват в файл, различен от нормалния изход на програмата.

Както обикновено се случва, повече знания носи повече възможности.