Tips en Trucs 2023

Bash in- en uitvoer omleiden

Redirection en piping zijn twee handige functies in bash scripting die sysadmins en ontwikkelaars vaak gebruiken. In deze tip bespreken we wat Bash redirection is en hoe je met redirection in Bash kunt werken met voorbeeldopdrachten.

Voordat we begrijpen hoe redirection werkt, is het belangrijk om te leren wat een file descriptor is.

Wat is een File descriptor

Wanneer je een bestand opent in Linux, krijgt elk bestand een geheel getal (integer) toegewezen en deze informatie wordt opgeslagen in de kernel. Op deze manier weet de kernel welke bestanden zijn geopend en welk proces de bestanden heeft geopend. Het toegewezen geheel getal noemen we de file descriptor (kortweg FD).

Standaard start elk programma met drie file descriptors:

Je kunt de bestandsdescriptors zien in de map /dev:

dany@pindabook:~> ls -l /dev/std*
lrwxrwxrwx 1 root root 15  4 jun 13:24 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15  4 jun 13:24 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15  4 jun 13:24 /dev/stdout -> /proc/self/fd/1

Stdin (FD0) krijgt invoer van het toetsenbord. stdout (FD1) en stderr (FD2) worden naar de terminal gestuurd. Wanneer je pipes en redirections gebruikt, kun je de manier veranderen waarop invoer wordt doorgegeven en uitvoer en fouten worden verzonden.

Uitvoer omleiden naar een bestand

Zoals eerder vermeld, wordt uitvoer (stdout) en fouten (stderr) van elk programma naar de terminal gestuurd. Je kunt de omleidingsoperator ">" gebruiken om de stdout en stderr naar een bestand te schrijven.

dany@pindabook:~> uname -mrs > pindanet.log
dany@pindabook:~> cat pindanet.log 
Linux 5.14.21-150400.24.63-default x86_64

Let op: Als je de ">" operator gebruikt, zal het bestand worden aangemaakt als het niet beschikbaar is. En als het bestand al bestaat, wordt het overschreven met nieuwe inhoud.

Je kunt ook het bestandsdescriptornummer voor stdout(1) vóór de omleidingsoperator gebruiken om uitvoer naar een bestand om te leiden.

dany@pindabook:~> uname -mrs 1> pindanet.log
dany@pindabook:~> cat pindanet.log 
Linux 5.14.21-150400.24.63-default x86_64

Zoals ik al zei, zal een enkele omleidingsoperator (>) alleen de inhoud van een bestand overschrijven als het bestand al bestaat. Als je echter de inhoud wilt toevoegen in plaats van overschrijven naar hetzelfde bestand, gebruik dan een dubbele omleidingsoperator (bijv. >>). Je kunt ook hier de stdout file descriptor(1) gebruiken.

dany@pindabook:~> whoami >> pindanet.log 
dany@pindabook:~> echo $SHELL 1>> pindanet.log 
dany@pindabook:~> cat pindanet.log 
Linux 5.14.21-150400.24.63-default x86_64
dany
/bin/bash

Hoe om te gaan met Stderr?

Elk programma genereert zowel uitvoer als fouten en beide worden naar de terminal gestuurd. In veel gevallen wil je de fout negeren of de fouten isoleren en omleiden naar een apart bestand om problemen op te lossen.

Laten we een eenvoudig voorbeeld nemen, het uitvoeren van de ls opdracht, om te zien hoe dit werkt. We proberen twee bestanden op te sommen waarvan er maar één aanwezig is. Je krijgt zals verwacht zowel de foutmelding als de uitvoer in de terminal.

dany@pindabook:~> ls -l pindanet.log pindanet.txt
ls: kan geen toegang krijgen tot 'pindanet.txt': Bestand of map bestaat niet
-rw-r--r-- 1 dany users 57  4 jun 13:34 pindanet.log

We voeren dezelfde ls opdracht opnieuw uit, maar deze keer wordt de uitvoer omgeleid naar een bestand. Zoals je kunt zien in de uitvoer leidt de > operator stdout(1) om naar een bestand, maar stderr(2) wordt naar de terminal gestuurd.

dany@pindabook:~> ls -l pindanet.log pindanet.txt > pindanet.log
ls: kan geen toegang krijgen tot 'pindanet.txt': Bestand of map bestaat niet

Gebruik de "2>" operator om stderr om te leiden naar een bestand. Om stderr(2) om te leiden, moet je de FD vermelden vóór de omleidingsoperator > waardoor die alleen de fout naar een bestand zal sturen.

dany@pindabook:~> ls -l pindanet.log pindanet.txt > pindanet.log 2> pindanet.err
dany@pindabook:~> cat pindanet.err
ls: kan geen toegang krijgen tot 'pindanet.txt': Bestand of map bestaat niet

Nu worden stdout en stderr naar aparte bestanden geschreven. Je kunt stdout en stderr ook naar één bestand sturen.

dany@pindabook:~> ls -l pindanet.log pindanet.txt &> pindanet.log
dany@pindabook:~> cat pindanet.log 
ls: kan geen toegang krijgen tot 'pindanet.txt': Bestand of map bestaat niet
-rw-r--r-- 1 dany users 0  4 jun 13:41 pindanet.log

Wat is /dev/null?

Null is een speciaal bestand dat invoer accepteert en de invoer weggooit en geen uitvoer produceert. Simpel gezegd, null gooit alles weg wat je er naartoe stuurt.

dany@pindabook:~> ls -l /dev/null
crw-rw-rw- 1 root root 1, 3  4 jun 13:24 /dev/null

Waarom is null belangrijk bij omleiding? Dat kun je je afvragen. In sommige gevallen wil je stdout of stderr niet weergeven of opslaan. In dat geval kun je stdout of stderr omleiden naar /dev/null, waardoor de uitvoerstroom wordt verwijderd.

dany@pindabook:~> date > /dev/null
dany@pindabook:~> date 1> /dev/null   # Stdout naar Null
dany@pindabook:~> dateee 2> /dev/null # Foutieve opdracht, Stderr naar Null
dany@pindabook:~> date &> /dev/null   # Stdout/Stderr naar Null

Invoer omleiden in Bash

Net zoals je de uitvoer en fouten omleidt naar een bestand, kun je ook invoer doorgeven aan een opdracht met de invoeromleidingsoperator (<).

Laten we beginnen met een eenvoudig programma voor het tellen van woorden. Hier leiden we de inhoud van het bestand pindanet.log om naar de opdracht wc om het aantal woorden te tellen.

dany@pindabook:~> wc -w < pindanet.log 
21

Je kunt ook de stdin bestandsdescriptor (0) doorgeven bij het omleiden van de invoer.

dany@pindabook:~> wc -w 0< pindanet.log 
21

Je kunt de omleidingsoperatoren voor invoer en uitvoer combineren zoals hieronder wordt gedemonstreerd.

dany@pindabook:~> wc -w < pindanet.log &> /tmp/pindanet.txt
dany@pindabook:~> cat /tmp/pindanet.txt 
21

Input redirection kan samen met een while-lus worden gebruikt om de inhoud van een bestand regel voor regel in te lezen.

Kijk eens naar het onderstaande voorbeeld. We geven het bestand /etc/passwd door als invoer voor de while opdracht. Hier leest de read opdracht de invoer regel voor regel en slaat het op in de variabele VAL en verderop in de lus wordt de conditie geschreven om te controleren of de gebruiker beschikbaar is.

Gebruik je favoriete teksteditor, in mijn geval nano:

dany@pindabook:~> nano pindanet.sh

en typ het volgende script:

#!/bin/bash
while read VAL; do   
  NAME=$(echo $VAL | awk -F  ":" '/1/ {print $1}' )
  if [[ $NAME = "dany" ]]
  then
   echo "Gebruiker gevonden"
  fi
done < /etc/passwd

Vervang "dany" in de bovenstaande code door een gebruikersnaam op je systeem. Sla het bestand op en sluit de teksteditor. Dit is geen optimale manier om de gebruiker te vinden, maar voor demonstratiedoeleinden is dit voldoende.

Maak het script nu uitvoerbaar en voer het uit:

dany@pindabook:~> chmod +x pindanet.sh 
dany@pindabook:~> ./pindanet.sh
Gebruiker gevonden
Bash Redirect