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

By rik

I Linux-system, när du initierar ett kommando, upprättas tre distinkta dataströmmar: stdin, stdout och stderr. Dessa strömmar ger dig möjligheten att övervaka och styra hur dina skript kommunicerar med systemet, inklusive omdirigering av input och output.

Dataströmmar: En förbindelse mellan två punkter

Om du är nybörjare i Linux- eller Unix-baserade miljöer, kommer du snart att stöta på koncepten stdin, stdout och stderr. Dessa representerar de tre standardströmmarna som aktiveras när ett Linux-kommando körs. Inom datateknik fungerar en ström som en kanal för dataöverföring. I det här fallet består informationen av text.

I likhet med vattenflöden, har även dataströmmar två definierade ändar – en källa och ett utflöde. Varje kommando du använder i Linux bidrar med en ände av varje ström. Den motsatta änden bestäms av det shell som initierade kommandot, som antingen är kopplat till terminalfönstret, dirigeras till en pipeline, eller omdirigeras till en fil eller ett annat kommando, beroende på kommandoraden som användes för att starta processen.

Standardströmmar i Linux

I Linux utgör stdin standardingångsströmmen, vilken tar emot text som input. Utmatningen som genereras av ett kommando dirigeras till skalet via stdout (standard ut)-strömmen. Eventuella felmeddelanden som kommandot kan producera skickas via stderr (standard fel)-strömmen.

Som du ser, finns det två utgångsströmmar – stdout och stderr – och en ingångsström, stdin. Eftersom felmeddelanden och normal utmatning har separata kanaler för att nå terminalfönstret, kan de hanteras individuellt.

Strömmar hanteras som filer

I Linux, behandlas strömmar, precis som i stort sett allt annat, som om de vore filer. Du kan läsa text från en fil och skriva text till en fil, vilket i båda fallen involverar en dataström. Konceptet att hantera en dataström som en fil är därför inte så främmande.

Varje fil som är relaterad till en process tilldelas ett unikt nummer som kallas filbeskrivare för att identifiera den. Närhelst en operation ska utföras på en fil, används filbeskrivaren för att särskilja filen.

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

0: stdin
1: stdout
2: stderr

Interaktion med pipelines och omdirigeringar

När man introducerar ett nytt koncept är det vanligt att först lära ut en förenklad version. Till exempel, i grammatik, får vi lära oss regeln ”i före e, utom efter c”. Men faktum är att det finns fler undantag från den regeln än det finns fall som följer den.

På samma sätt, när vi diskuterar stdin, stdout och stderr, är det lockande att tro att en process varken vet eller bryr sig om var dess tre standardströmmar slutar. Men 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 input kommer från tangentbordet eller överförs från en annan process?

I själva verket vet en process – eller kan åtminstone ta reda på det om den väljer att kontrollera – och kan ändra sitt beteende om programvaruutvecklaren har implementerat den funktionen.

Denna beteendeförändring är lätt att se. Prova dessa två kommandon:

ls

ls | cat

Kommandot `ls` beter sig annorlunda beroende på om dess output (stdout) dirigeras till ett annat kommando. `ls` växlar till en enkelkolumnoutput; det är inte `cat` som gör konverteringen. Samma sak händer om dess output omdirigeras:

ls > capture.txt

cat capture.txt

Omdirigering av stdout och stderr

Det finns en fördel med att felmeddelanden skickas via en egen ström. Det gör att vi kan dirigera ett kommandos output (stdout) till en fil, medan felmeddelanden (stderr) fortfarande visas i terminalfönstret. Du kan reagera på felen medan de uppstår och de stör inte den fil som stdout har omdirigerats till.

Skriv in följande text i en texteditor 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 skriver ut text i terminalfönstret via stdout-strömmen. Den andra raden försöker nå en icke-existerande fil, vilket ger ett felmeddelande som skickas via stderr.

Kör skriptet med det här kommandot:

./error.sh

Vi ser att både stdout och stderr har visats i terminalfönstret.

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

./error.sh > capture.txt

Felmeddelandet som skickas via stderr visas fortfarande i terminalfönstret. Vi kan kontrollera filens innehåll för att se om stdout-outputen hamnade där.

cat capture.txt

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

Omdirigeringssymbolen `>` fungerar med stdout som standard. Du kan ange vilken standardutgångsström du vill omdirigera med hjälp av en av de numeriska filbeskrivarna.

För att omdirigera stdout explicit, använd den här omdirigeringsanvisningen:

1>

För att omdirigera stderr explicit, använd den här omdirigeringsanvisningen:

2>

Låt oss göra om testet och den här gången använder vi `2>`:

./error.sh 2> capture.txt

Felmeddelandet omdirigeras och stdout-meddelandet skickas till terminalfönstret:

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

Omdirigering av både stdout och stderr

Om vi kan omdirigera antingen stdout eller stderr separat, borde vi också kunna omdirigera dem samtidigt till två olika filer?

Ja, det kan vi. Det här kommandot 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

Eftersom båda output-strömmarna har omdirigerats till filer, finns det ingen synlig output i terminalfönstret. Vi återgår till kommandoraden som om ingenting hade hänt.

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

cat capture.txt
cat error.txt

Omdirigering av stdout och stderr till samma fil

Det är praktiskt att ha alla standardutgångsströmmar riktade till egna filer. Den enda återstående kombinationen vi kan göra är att skicka både stdout och stderr till samma fil.

Detta kan åstadkommas med följande kommando:

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

Låt oss gå igenom det steg för steg:

./error.sh: Startar skriptfilen `error.sh`.
`> capture.txt`: Omdirigerar stdout-strömmen till filen `capture.txt`. `>` är en förkortning av `1>`.
`2>&1`: Detta använder `&>`-omdirigeringsinstruktionen, vilket låter dig ange för shell att dirigera en ström till samma destination som en annan ström. I detta fall säger vi: ”Omdirigera ström 2, stderr, till samma destination som ström 1, stdout, omdirigeras till.”

Det finns ingen synlig output. Det är bra.

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 samma destinationsfil.

För att tyst dölja outputen av en ström, omdirigera den till `/dev/null`.

Identifiera omdirigeringar i skript

Vi har diskuterat hur ett kommando kan avgöra om någon av strömmarna omdirigeras och hur de kan anpassa sig till det. Kan vi åstadkomma detta i våra egna skript? Ja, och det är en enkel teknik att förstå och använda.

Skriv följande text i en texteditor 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 viktiga delen är testet inom hakparenteserna. Alternativet `-t` (terminal) returnerar true (0) om filen som är kopplad till filbeskrivaren är kopplad till terminalfönstret. Vi använder filbeskrivaren `0` som argument för testet, vilket representerar stdin.

Om stdin är kopplad till ett terminalfönster, kommer testet att vara sant. Om stdin är kopplad till en fil eller en pipeline, misslyckas testet.

Vi kan använda vilken textfil som helst för att generera input till skriptet. Här använder vi en fil som heter `dummy.txt`.

./input.sh 

Outputen visar att skriptet känner igen att inputen inte kommer från ett tangentbord utan från en fil. Du kan anpassa ditt skripts beteende efter behov.

Det var med filomdirigering, låt oss prova med en pipeline.

cat dummy.txt | ./input.sh

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

Låt oss köra skriptet utan pipelines eller omdirigeringar.

./input.sh

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

För att göra samma sak med utgångsströmmen behöver vi ett nytt skript. Skriv in följande i en texteditor 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 stora skillnaden i det här skriptet är testet inom hakparenteserna. Vi använder siffran 1 för att representera filbeskrivaren för stdout.

Låt oss testa det. Vi skickar outputen genom `cat`.

./output | cat

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

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

./output.sh > capture.txt

Det finns ingen output i terminalfönstret, vi återgår tyst till kommandoprompten. Som förväntat.

Vi kan undersöka filen `capture.txt` för att se vad som har fångats. 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 pipelines eller omdirigeringar, bör det känna igen att stdout levereras direkt till terminalfönstret.

./output.sh

Och det är precis vad vi ser.

Medvetna strömmar

Genom att veta hur du avgör om dina skript är kopplade till ett terminalfönster, en pipeline eller är omdirigerade, kan du anpassa deras beteende därefter.

Loggning och diagnostisk output kan variera i detaljrikedom, beroende på om de visas på skärmen eller går till en fil. Felmeddelanden kan loggas till en annan fil än den normala programoutputen.

Som vanligt ger mer kunskap fler alternativ.