Vad är stdin, stdout och stderr på Linux?

stdin, stdout och stderr är tre dataströmmar som skapas när du startar ett Linux-kommando. Du kan använda dem för att se om dina skript skickas eller omdirigeras. Vi visar dig hur.

Strömmar förenar två poäng

Så fort du börjar lära dig om Linux och Unix-liknande operativsystem kommer du att stöta på termerna stdin, stdout och stederr. Dessa är tre standardströmmar som upprättas när ett Linux-kommando exekveras. Inom datorer är en ström något som kan överföra data. När det gäller dessa strömmar är den informationen text.

Dataströmmar har, precis som vattenströmmar, två ändar. De har en källa och ett utflöde. Vilket Linux-kommando du än använder ger en ände av varje ström. Den andra änden bestäms av skalet som startade kommandot. Den änden kommer att kopplas till terminalfönstret, kopplas till ett rör eller omdirigeras till en fil eller annat kommando, enligt kommandoraden som startade kommandot.

Linux standardströmmar

I Linux är stdin standardinmatningsströmmen. Detta accepterar text som indata. Textutmatning från kommandot till skalet levereras via stdout-strömmen (standard ut). Felmeddelanden från kommandot skickas via stderr-strömmen (standardfel).

Så du kan se att det finns två utströmmar, stdout och stderr, och en ingångsström, stdin. Eftersom felmeddelanden och normal utmatning var och en har sin egen kanal för att föra dem till terminalfönstret, kan de hanteras oberoende av varandra.

Strömmar hanteras som filer

Strömmar i Linux – som nästan allt annat – behandlas som om de vore filer. Du kan läsa text från en fil och du kan skriva text i en fil. Båda dessa åtgärder involverar en dataström. Så konceptet att hantera en ström av data som en fil är inte så mycket av en sträcka.

Varje fil som är associerad med en process tilldelas ett unikt nummer för att identifiera den. Detta är känt som filbeskrivningen. Närhelst en åtgärd krävs för att utföras på en fil, filbeskrivningen används för att identifiera filen.

Dessa värden används alltid för stdin, stdout och stderr:

0: stdin
1: Stdout
2: stderr

Reagerar på rör och omdirigeringar

För att underlätta någons introduktion till ett ämne är en vanlig teknik att lära ut en förenklad version av ämnet. Till exempel, med grammatik, får vi veta att regeln är ”I före E, utom efter C.” Men faktiskt där är fler undantag från denna regel än det finns fall som lyder det.

På liknande sätt, när man talar om stdin, stdout och stderr, är det bekvämt att tro ut det accepterade axiomet att en process varken vet eller bryr sig om var dess tre standardströmmar avslutas. Bör en process bry sig om huruvida dess utdata går till terminalen eller omdirigeras till en fil? Kan den ens avgöra om dess inmatning kommer från tangentbordet eller överförs till den från en annan process?

Egentligen vet en process – eller åtminstone kan den ta reda på det om den väljer att kontrollera – och den kan ändra sitt beteende i enlighet med detta om programvaruförfattaren beslutade att lägga till den funktionen.

Vi kan se denna förändring i beteende mycket lätt. Prova dessa två kommandon:

ls

ls | cat

Kommandot ls beter sig annorlunda om dess utdata (stdout) överförs till ett annat kommando. Det är ls som växlar till en enda kolumnutgång, det är inte en konvertering som utförs av cat. Och jag gör samma sak om dess utdata omdirigeras:

ls > capture.txt

ls > capture.txt i ett terminalfönster” 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 decoding=

Omdirigerar stdout och stderr

Det finns en fördel med att få felmeddelanden levererade av en dedikerad ström. Det betyder att vi kan omdirigera ett kommandos utdata (stdout) till en fil och fortfarande se eventuella felmeddelanden (stderr) i terminalfönstret. Du kan reagera på felen om du behöver, allt eftersom de uppstår. Det stoppar också felmeddelandena från att kontaminera filen som stdout har omdirigerats till.

Skriv in följande text i en editor och spara den i en fil som heter error.sh.

#!/bin/bash

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

Gör skriptet körbart med detta kommando:

chmod +x error.sh

Den första raden i skriptet ekar text till terminalfönstret, via stdout-strömmen. Den andra raden försöker komma åt en fil som inte finns. Detta kommer att generera ett felmeddelande som levereras via stderr.

Kör skriptet med detta kommando:

./error.sh

Vi kan se att båda strömmar av output, stdout och stderr, har visats i terminalfönstren.

Låt oss försöka omdirigera utdata till en fil:

./error.sh > capture.txt

./error.sh > capture.txt i ett terminalfönster” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Felmeddelandet som levereras via stderr skickas fortfarande till terminalfönstret.  Vi kan kontrollera innehållet i filen för att se om stdout-utgången gick till filen.</p>
<pre>cat capture.txt</pre>
<p><img loading=

Utdata från stdin omdirigerades till filen som förväntat.

Omdirigeringssymbolen > fungerar med stdout som standard. Du kan använda en av de numeriska filbeskrivningarna för att indikera vilken standardutgångsström du vill omdirigera.

För att uttryckligen omdirigera stdout, använd denna omdirigeringsinstruktion:

1>

För att uttryckligen omdirigera stderr, använd denna omdirigeringsinstruktion:

2>

Låt oss göra vårt test igen, och den här gången använder vi 2>:

./error.sh 2> capture.txt

./error.sh 2> capture.txt i ett terminalfönster” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Felmeddelandet omdirigeras och stdout-ekomeddelandet skickas till terminalfönstret:</p>
<p ><img klass=

Stderr-meddelandet finns i capture.txt som förväntat.

Omdirigerar både stdout och stderr

Visst, om vi kan omdirigera antingen stdout eller stderr till en fil oberoende av varandra, borde vi kunna omdirigera dem båda samtidigt, till två olika filer?

Ja det kan vi. Detta kommando kommer att dirigera stdout till en fil som heter capture.txt och stderr till en fil som heter error.txt.

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

./error.sh 1> capture.txt 2> error.txt i ett terminalfönster” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Eftersom både utdataströmmar – standardutdata och standardfel – omdirigeras till filer, finns det ingen synlig utdata i terminalfönstret.  Vi återgår till kommandoraden som om ingenting har hänt.</p>
<p><img loading=

Låt oss kontrollera innehållet i varje fil:

cat capture.txt
cat error.txt

Omdirigerar stdout och stderr till samma fil

Det är snyggt, vi har alla standardutdataströmmar som går till sin egen dedikerade fil. Den enda andra kombinationen vi kan göra är att skicka både stdout och stderr till samma fil.

Vi kan uppnå detta med följande kommando:

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

Låt oss bryta ner det.

./error.sh: Startar skriptfilen error.sh.
> capture.txt: Omdirigerar stdout-strömmen till capture.txt-filen. > är förkortning för 1>.
2>&1: Detta använder &> omdirigeringsinstruktionen. Den här instruktionen låter dig säga åt skalet att få en ström till samma destination som en annan ström. I det här fallet säger vi ”omdirigera ström 2, stderr, till samma destination som ström 1, stdout, omdirigeras till.”

./error.sh > capture.txt 2&>1 i ett terminalfönster” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Det finns ingen synlig utdata.  Det är uppmuntrande.</p>
<p><img loading=

Låt oss kontrollera filen capture.txt och se vad som finns i den.

cat capture.txt

Både stdout- och stderr-strömmarna har omdirigerats till en enda destinationsfil.

För att få utdata från en ström omdirigerad och tyst kastad, rikta utdata till /dev/null.

Upptäcker omdirigering i ett skript

Vi diskuterade hur ett kommando kan upptäcka om någon av strömmarna omdirigeras och kan välja att ändra sitt beteende därefter. Kan vi åstadkomma detta i våra egna manus? Ja det kan vi. Och det är en mycket enkel teknik att förstå och använda.

Skriv in följande text i en editor och spara den som input.sh.

#!/bin/bash

if [ -t 0 ]; then

  echo stdin coming from keyboard
 
else

  echo stdin coming from a pipe or a file
 
fi

Använd följande kommando för att göra det körbart:

chmod +x input.sh

Den smarta delen är testa inom hakparenteserna. Alternativet -t (terminal) returnerar true (0) om filen är associerad med filbeskrivningen avslutas i terminalfönstret. Vi har använt filbeskrivningen 0 som argument för testet, som representerar stdin.

Om stdin är ansluten till ett terminalfönster kommer testet att visa sig sant. Om stdin är ansluten till en fil eller ett rör misslyckas testet.

Vi kan använda vilken bekväm textfil som helst för att generera input till skriptet. Här använder vi en som heter 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

Skriptet känner igen att dess input överförs till det. Eller mer exakt, den känner igen att stdin-strömmen inte är ansluten till ett terminalfönster.

Låt oss köra skriptet med varken pipes eller omdirigeringar.

./input.sh

Stdin-strömmen är ansluten till terminalfönstret, och skriptet rapporterar detta i enlighet med detta.

För att kontrollera samma sak med utgångsströmmen behöver vi ett nytt skript. Skriv in följande i en editor och spara den som 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

Använd följande kommando för att göra det körbart:

chmod +x input.sh

Den enda betydande förändringen av detta skript är i testet inom hakparenteser. Vi använder siffran 1 för att representera filbeskrivningen för stdout.

Låt oss prova det. Vi skickar utgången genom katten.

./output | cat

Skriptet känner igen att dess utdata inte går direkt till ett terminalfönster.

Vi kan också testa skriptet genom att omdirigera utdata till en fil.

./output.sh > capture.txt

Det finns ingen utdata till terminalfönstret, vi återgår tyst till kommandotolken. Som vi förväntade oss.

Vi kan titta inuti capture.txt-filen för att se vad som fångades. Använd följande kommando för att göra det.

cat capture.sh

Återigen, det enkla testet i vårt skript upptäcker att stdout-strömmen inte skickas direkt till ett terminalfönster.

Om vi ​​kör skriptet utan några pipes eller omdirigeringar, bör det upptäcka att stdout levereras direkt till terminalfönstret.

./output.sh

Och det är precis vad vi ser.

Strömmar av medvetande

Genom att veta hur man avgör om dina skript är anslutna till terminalfönstret, eller ett rör, eller omdirigeras, kan du justera deras beteende därefter.

Loggning och diagnostisk utdata kan vara mer eller mindre detaljerad, beroende på om den går till skärmen eller till en fil. Felmeddelanden kan loggas till en annan fil än den vanliga programutgången.

Som vanligtvis är fallet ger mer kunskap fler alternativ.