Wat zijn stdin, stdout en stderr op Linux?

Terminalvenster op een Linux-computer
Fatmawati Achmad Zaenuri / Shutterstock.com

stdin, stdout, en stderr zijn drie datastromen die worden gemaakt wanneer u een Linux-opdracht start. U kunt ze gebruiken om te zien of uw scripts worden doorgesluisd of omgeleid. We laten je zien hoe.

Streams voegen zich bij twee punten

Zodra je meer leert over Linux en Unix-achtige besturingssystemen, kom je de termen tegen stdin, stdout, en stederr. Dit zijn drie standaard streams die tot stand worden gebracht wanneer een Linux-opdracht wordt uitgevoerd. Bij computers is een stream iets dat gegevens kan overbrengen. In het geval van deze streams zijn die gegevens tekst.

Datastromen hebben, net als waterstromen, twee uiteinden. Ze hebben een bron en een uitstroom. Welke Linux-opdracht u ook gebruikt, biedt één uiteinde van elke stream. Het andere uiteinde wordt bepaald door de shell waarmee de opdracht is gestart. Dat uiteinde wordt verbonden met het terminalvenster, verbonden met een pijp of omgeleid naar een bestand of een andere opdracht, volgens de opdrachtregel die de opdracht heeft gestart.

De Linux Standard Streams

In Linux, stdin is de standaard invoerstroom. Dit accepteert tekst als invoer. Tekstuitvoer van het commando naar de shell wordt geleverd via de stdout (standaard uit) stream. Foutmeldingen van de opdracht worden verzonden via de stderr (standaardfout) stream.

U kunt dus zien dat er twee outputstromen zijn, stdout en stderr, en een invoerstroom, stdin. Omdat foutmeldingen en normale uitvoer elk hun eigen kanaal hebben om ze naar het terminalvenster te leiden, kunnen ze onafhankelijk van elkaar worden afgehandeld.

Streams worden behandeld als bestanden

Streams in Linux worden – net als bijna al het andere – behandeld alsof het bestanden zijn. U kunt tekst uit een bestand lezen en u kunt tekst in een bestand schrijven. Bij beide acties is een stroom gegevens betrokken. Dus het concept van het verwerken van een gegevensstroom als een bestand is niet zo heel ingewikkeld.

Elk bestand dat aan een proces is gekoppeld, krijgt een uniek nummer toegewezen om het te identificeren. Dit staat bekend als de bestandsdescriptor. Telkens wanneer een actie op een bestand moet worden uitgevoerd, wordt de bestandsdescriptor gebruikt om het bestand te identificeren.

Deze waarden worden altijd gebruikt voor stdin, stdout, en stderr:

  • 0: stdin
  • 1: stdout
  • 2: stderr

Reageren op leidingen en omleidingen

Om iemands introductie tot een onderwerp te vergemakkelijken, is een veelgebruikte techniek het aanleren van een vereenvoudigde versie van het onderwerp. Met grammatica wordt ons bijvoorbeeld verteld dat de regel is “I voor E, behalve na C.” Maar eigenlijk zijn er meer uitzonderingen op deze regel dan dat er gevallen zijn die eraan voldoen.

In dezelfde geest, als we het er over hebben stdin, stdout, en stderr het is gemakkelijk om het geaccepteerde axioma uit te dragen dat een proces niet weet en ook niet kan schelen waar de drie standaardstromen worden beëindigd. Moet een proces zich zorgen maken of de uitvoer naar de terminal gaat of wordt omgeleid naar een bestand? Kan het zelfs zien of de invoer van het toetsenbord komt of vanuit een ander proces wordt ingevoerd?

In feite weet een proces het – of kan het er tenminste achter komen, mocht het ervoor kiezen om te controleren – en het kan zijn gedrag dienovereenkomstig veranderen als de softwareauteur besluit die functionaliteit toe te voegen.

We kunnen deze gedragsverandering heel gemakkelijk zien. Probeer deze twee opdrachten:

ls

ls in een terminalvenster

ls | cat

ls uitvoer in een terminalvenster

De ls commando gedraagt ​​zich anders als zijn output (stdout) wordt doorgesluisd naar een ander commando. Het is ls die overschakelt naar een uitvoer met één kolom, het is geen conversie uitgevoerd door cat. En ls doet hetzelfde als de uitvoer wordt omgeleid:

ls > capture.txt

ls> capture.txt in een terminalvenster” 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 en stderr omleiden

Er is een voordeel als foutmeldingen worden afgeleverd door een speciale stream. Het betekent dat we de uitvoer van een commando kunnen omleiden (stdout) naar een bestand en zie nog steeds eventuele foutmeldingen (stderr) in het terminalvenster. U kunt indien nodig op de fouten reageren zodra ze zich voordoen. Het voorkomt ook dat de foutmeldingen het bestand dat stdout is omgeleid naar.

Typ de volgende tekst in een editor en sla deze op in een bestand met de naam error.sh.

#!/bin/bash

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

Maak het script uitvoerbaar met deze opdracht:

chmod +x error.sh

De eerste regel van het script echoot tekst naar het terminalvenster, via de stdout stroom. De tweede regel probeert toegang te krijgen tot een bestand dat niet bestaat. Dit genereert een foutmelding die wordt afgeleverd via stderr.

Voer het script uit met deze opdracht:

./error.sh

./error.sh in een terminalvenster

We kunnen zien dat beide outputstromen, stdout en stderr, zijn weergegeven in de terminalvensters.

uitvoer van error.sh script in een terminalvenster

Laten we proberen de uitvoer om te leiden naar een bestand:

./error.sh > capture.txt

./error.sh> capture.txt in een terminalvenster” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>De foutmelding die wordt afgeleverd via <code>stderr</code> wordt nog steeds naar het terminalvenster gestuurd.  We kunnen de inhoud van het bestand controleren om te zien of het <code>stdout</code> uitvoer ging naar het bestand.</p>
<pre>cat capture.txt</pre>
<p><img loading=

De output van stdin werd omgeleid naar het bestand zoals verwacht.

inhoud van capture.txt in een terminalvenster

De > omleidingssymbool werkt met stdout standaard. U kunt een van de numerieke bestandsdescriptors gebruiken om aan te geven welke standaard uitvoerstroom u wilt omleiden.

Om expliciet om te leiden stdout, gebruik dan deze omleidingsinstructie:

1>

Om expliciet om te leiden stderr, gebruik dan deze omleidingsinstructie:

2>

Laten we het opnieuw proberen, en deze keer gebruiken we 2>:

./error.sh 2> capture.txt

./error.sh 2> capture.txt in een terminalvenster” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Het foutbericht wordt omgeleid en het <code>stdout</code> <code>echo</code>  bericht wordt naar het terminalvenster gestuurd:</p>
<p><img decoding=

De stderr bericht is in capture.txt zoals verwacht.

inhoud van het bestand capture.txt in een terminalvenster

Omleiden van zowel stdout als stderr

Zeker, als we een van beide kunnen omleiden stdout of stderr naar een bestand onafhankelijk van elkaar, zouden we ze allebei tegelijkertijd naar twee verschillende bestanden moeten kunnen omleiden?

Ja dat kunnen we. Dit commando zal leiden stdout naar een bestand met de naam capture.txt en stderr naar een bestand met de naam error.txt.

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

./error.sh 1> capture.txt 2> error.txt in een terminalvenster” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Omdat beide outputstromen – standaarduitvoer en standaardfout – worden omgeleid naar bestanden, is er geen zichtbare uitvoer in het terminalvenster.  We keren terug naar de opdrachtregelprompt alsof er niets is gebeurd.</p>
<p><img loading=

Laten we de inhoud van elk bestand controleren:

cat capture.txt
cat error.txt

inhoud van capture.txt en error.txt in een terminalvenster

Stdout en stderr omleiden naar hetzelfde bestand

Dat is netjes, we hebben elk van de standaard uitvoerstreams naar zijn eigen speciale bestand. De enige andere combinatie die we kunnen doen, is beide verzenden stdout en stderr naar hetzelfde bestand.

We kunnen dit bereiken met het volgende commando:

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

Laten we dat opsplitsen.

  • ./error.sh: Start het scriptbestand error.sh.
  • > capture.txt: Leidt het stdout stream naar het capture.txt-bestand. > is een afkorting voor 1>.
  • 2> & 1: Dit gebruikt de &> redirect-instructie. Met deze instructie kun je de shell vertellen dat de ene stream naar dezelfde bestemming moet gaan als de andere stream. In dit geval zeggen we ‘redirect stream 2, stderr, naar dezelfde bestemming die stream 1, stdout, wordt omgeleid naar. “

./error.sh> capture.txt 2 &> 1 in een terminalvenster” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Er is geen zichtbare output.  Dat is bemoedigend.</p>
<p><img loading=

Laten we het capture.txt-bestand bekijken en kijken wat erin zit.

cat capture.txt

inhoud van capture.txt in een terminalvenster

Beide stdout en stderr streams zijn omgeleid naar een enkel doelbestand.

Als u de uitvoer van een stream wilt laten omleiden en stilletjes wilt weggooien, richt u de uitvoer naar /dev/null.

Omleiding binnen een script detecteren

We hebben besproken hoe een commando kan detecteren of een van de streams wordt omgeleid, en ervoor kan kiezen om zijn gedrag dienovereenkomstig aan te passen. Kunnen we dit bereiken in onze eigen scripts? Ja dat kunnen we. En het is een heel gemakkelijke techniek om te begrijpen en toe te passen.

Typ de volgende tekst in een editor en sla deze op als input.sh.

#!/bin/bash

if [ -t 0 ]; then

  echo stdin coming from keyboard
 
else

  echo stdin coming from a pipe or a file
 
fi

Gebruik de volgende opdracht om het uitvoerbaar te maken:

chmod +x input.sh

Het slimme is de test tussen de vierkante haken. De -t (terminal) optie retourneert true (0) als het bestand dat is gekoppeld aan de bestandsdescriptor eindigt in het terminalvenster. We hebben de bestandsdescriptor 0 gebruikt als het argument voor de test, wat staat voor stdin.

Als stdin is verbonden met een terminalvenster, zal de test waar blijken te zijn. Als stdin is verbonden met een bestand of een pijp, zal de test mislukken.

We kunnen elk handig tekstbestand gebruiken om invoer voor het script te genereren. Hier gebruiken we een genaamd dummy.txt.

./input.sh < dummy.txt

./input.sh <dummy.txt in een terminalvenster

De uitvoer laat zien dat het script herkent dat de invoer niet van een toetsenbord komt, maar van een bestand. Als u ervoor kiest, kunt u het gedrag van uw script dienovereenkomstig aanpassen.

uitvoer van script in een terminalvenster

Dat was met een bestandsomleiding, laten we het proberen met een pijp.

cat dummy.txt | ./input.sh

kat dummy.txt |  ./input.sh in een terminalvenster

Het script herkent dat zijn invoer erin wordt doorgesluisd. Of beter gezegd, het erkent nogmaals dat de stdin stream is niet verbonden met een terminalvenster.

uitvoer van script in een terminalvenster

Laten we het script uitvoeren zonder leidingen of omleidingen.

./input.sh

./input.sh in een terminalvenster

De stdin stream is verbonden met het terminalvenster en het script rapporteert dit dienovereenkomstig.

Om hetzelfde te controleren met de uitvoerstroom, hebben we een nieuw script nodig. Typ het volgende in een editor en sla het op als 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

Gebruik de volgende opdracht om het uitvoerbaar te maken:

chmod +x input.sh

De enige significante wijziging in dit script is de test tussen vierkante haken. We gebruiken het cijfer 1 om de bestandsdescriptor voor te stellen stdout.

Laten we het uitproberen. We leiden de output door cat.

./output | cat

./output |  kat in een terminalvenster

Het script herkent dat de uitvoer ervan niet rechtstreeks naar een terminalvenster gaat.

scriptuitvoer in een terminalvenster

We kunnen het script ook testen door de uitvoer naar een bestand om te leiden.

./output.sh > capture.txt

./output.sh> capture.txt in een terminalvenster” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);” onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Er is geen uitvoer naar het terminalvenster, we worden stilletjes teruggestuurd naar de opdrachtprompt.  Zoals we hadden verwacht.</p>
<p><img loading=

We kunnen in het bestand capture.txt kijken om te zien wat er is vastgelegd. Gebruik hiervoor het volgende commando.

cat capture.sh

cat capture.sh in een terminalvenster

Nogmaals, de eenvoudige test in ons script detecteert dat het stdout stream wordt niet rechtstreeks naar een terminalvenster gestuurd.

Als we het script uitvoeren zonder leidingen of omleidingen, zou het dat moeten detecteren stdout wordt rechtstreeks in het terminalvenster afgeleverd.

./output.sh

./output.sh in een terminalvenster

En dat is precies wat we zien.

scriptuitvoer in een terminalvenster

Bewustzijnsstromen

Als u weet of uw scripts zijn verbonden met het terminalvenster, of een pipe, of worden omgeleid, kunt u hun gedrag dienovereenkomstig aanpassen.

Logboekregistratie en diagnostische uitvoer kunnen meer of minder gedetailleerd zijn, afhankelijk van of het naar het scherm of naar een bestand gaat. Foutmeldingen kunnen worden gelogd naar een ander bestand dan de normale programma-uitvoer.

Zoals gewoonlijk brengt meer kennis meer mogelijkheden met zich mee.

Nieuwste artikelen

Gerelateerde artikelen