Bash Scripts – Deel 4 – Invoer en Uitvoer

Mikhail Raevskiy
Mikhail Raevskiy

Follow

28 jul, 2020 – 11 min read

De vorige keer, in deel 3 van deze bash scripting serie, hebben we het gehad over command line parameters en switches. Ons onderwerp vandaag is input, output, en alles wat daarmee te maken heeft.

Foto door Ben Kolde op Unsplash

Je bent al bekend met twee methodes om te werken met wat command-regel scripts uitvoeren:

  • De uitvoergegevens op het scherm weergeven.
  • Uitvoer omleiden naar een bestand.

Soms moet er iets op het scherm worden getoond, en moet er iets naar een bestand worden geschreven, dus je moet begrijpen hoe invoer en uitvoer in Linux worden afgehandeld, wat betekent dat je moet leren hoe je de resultaten van de scripts daarheen kunt sturen waar je ze nodig hebt. Laten we beginnen met het hebben over standaard bestandsdescriptors.

Alles in Linux zijn bestanden, inclusief invoer en uitvoer. Het besturingssysteem identificeert bestanden met behulp van descriptors.

Elk proces mag maximaal negen open bestandsdescriptors hebben. De bash shell reserveert de eerste drie descriptors met IDs 0, 1, en 2. Hier is wat ze betekenen.

  • 0STDIN– standaard invoer stream.
  • 1STDOUT– standaard uitvoer stream.
  • 2STDERR– standaard foutstroom.

Deze drie speciale descriptors behandelen de invoer en uitvoer van het script.
Je moet de standaard streams goed begrijpen. Ze kunnen worden vergeleken met het fundament waarop de interactie van scripts met de buitenwereld is gebouwd. Laten we eens kijken naar de details over hen.

STDIN

STDIN– dit is de standaard invoer van de shell. Voor een terminal is de standaard input het toetsenbord. Wanneer scripts het input redirection karakter gebruiken – <, vervangt Linux de standaard input file descriptor door de file die in het commando is opgegeven. Het systeem leest het bestand en verwerkt de gegevens alsof ze vanaf het toetsenbord zijn ingevoerd.

Veel bash commando’s nemen invoer aan van STDIN, tenzij er een bestand op de opdrachtregel is gespecificeerd om gegevens uit op te halen. Dit geldt bijvoorbeeld voor een team cat.

Wanneer u een commando cat op de commandoregel invoert zonder parameters op te geven, accepteert het invoer van STDIN. Nadat u de volgende regel hebt ingevoerd, drukt hij cat gewoon af op het scherm.

STDOUT

STDOUT– de standaard uitvoer van de shell. Standaard is dit het scherm. De meeste bash commando’s voeren gegevens uit STDOUT naar de console, waardoor deze in de console verschijnen. De gegevens kunnen naar een bestand worden omgeleid door ze aan de inhoud te koppelen met het commando >>.

Dus, we hebben een bepaald gegevensbestand, waaraan we andere gegevens kunnen toevoegen met behulp van dit commando:

pwd >> myfile

Wat het uitvoert pwdwordt toegevoegd aan het bestand myfile, terwijl de gegevens die er al in staan nergens heen zullen gaan.

raevskym@DESKTOP-JNF3L6H:~/bash_course$ pwd >> myfile
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
this is old file content
/home/raevskym/Desktop

Tot zover alles goed, maar wat als je iets probeert te doen zoals hieronder, waarbij je toegang krijgt tot een niet-bestaand bestandxfile, en dit alles zo bedenkt datmyfilede foutmelding in het bestand komt.

ls –l xfile > myfile

Na het uitvoeren van dit commando zien we foutmeldingen op het scherm.

raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l xfile > myfile
ls: cannot access '-l': No sudh file or directory
ls: cannot access 'xfile': No such file or directory

Een poging om toegang te krijgen tot een niet-bestaand bestand genereert een fout, maar de shell heeft de foutmeldingen niet doorgestuurd naar het bestand, zodat ze worden weergegeven. Maar we wilden dat de foutmeldingen naar het bestand gingen. Wat moeten we doen? Het antwoord is eenvoudig – gebruik de derde standaard descriptor.

STDERR

STDERR is de standaard foutstroom van de shell. Standaard wijst dit handvat naar hetzelfde waar het naar wijst STDOUT, daarom zien we een bericht op het scherm als er een fout optreedt.

Zo, stel dat u foutmeldingen wilt omleiden naar, zeg, een logbestand, of ergens anders, in plaats van ze op het scherm af te drukken.

Foutstroom omleiden

Zoals u al weet, is een bestandsdescriptor voorSTDERR 2. We kunnen fouten omleiden door deze descriptor voor het omleidingcommando te plaatsen:

ls -l xfile 2>myfile
cat ./myfile

De foutmelding gaat nu naar het bestand myfile.

raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l xfile 2> myfile
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
ls: cannot access 'xfile': No such flie or directory

Fout- en uitvoerstromen omleiden

Bij het schrijven van commandline-scripts kan een situatie ontstaan waarin u zowel het omleiden van foutberichten als het omleiden van standaarduitvoer moet organiseren. Om dit te bereiken, moet u de omleidingscommando’s gebruiken voor de overeenkomstige descriptors, die de bestanden aangeven waar de fouten en de standaarduitvoer naartoe moeten:

ls –l myfile xfile anotherfile 2> errorcontent 1> correctcontent

Laten we eens wat dichterbij kijken:

raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l myfile xfile anotherfile 2> errorcontent 1> correctcontent
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./correctcontent
myfile
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./errorcontent
ls: cannot access '-l': No sudh file or directory
ls: cannot access 'xfile': No such file or directory
ls: cannot access 'anotherfile': No such file or directory

De shell zal wat het commandolsnormaal stuurt naarSTDOUTeen bestandcorrectcontentdoor zijn constructie1> omleiden. De foutmeldingen dieSTDERRin het bestand zouden zijn terechtgekomen, zijnerrorcontentdoor het omleidingscommando2>.

Indien nodig, kunnen enSTDERR, enSTDOUT naar hetzelfde bestand worden omgeleid met het commando&>:

raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l myfile xfile anotherfile &> content
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./content
ls: cannot access '-l': No sudh file or directory
ls: cannot access 'xfile': No such file or directory
ls: cannot access 'anotherfile': No such file or directory
myfile

Na het uitvoeren van het commando staat wat bedoeld is voorSTDERRenSTDOUT in het bestandcontent.

Uitvoer omleiden in scripts

Er zijn twee methoden voor het omleiden van uitvoer in opdrachtregelscripts:

  • Tijdelijke omleiding, of het omleiden van de uitvoer van één regel.
  • Permanent omleiden, of het omleiden van alle of een deel van de uitvoer in het script.

Tijdelijk omleiden van uitvoer

In een script kunt u de uitvoer van een enkele regel omleiden naar STDERR. Om dit te doen, is het voldoende om het redirection commando te gebruiken, met specificatie van een descriptor STDERR, en voor het descriptornummer moet je een ampersand ( &) symbool zetten :

#!/bin/bash
echo "This is an error" >&2
echo "This is normal output"

Als u het script uitvoert, komen beide regels op het scherm, omdat, zoals u al weet, fouten standaard op dezelfde plaats worden weergegeven als normale gegevens.

raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
This is an error
This is normal output

Laten we het script zo uitvoeren dat de uitvoerSTDERRnaar een bestand gaat.

./myscript 2> myfile

Zoals u kunt zien, wordt nu de gebruikelijke uitvoer naar de console gedaan, en worden foutmeldingen naar het bestand gestuurd.

raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 2> content
This is normal output
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./content
This is an error

Doorlopende uitvoer

Als een script veel weergegeven gegevens moet omleiden, is het echoonhandig om aan elke aanroep het juiste commando toe te voegen. In plaats daarvan kunt u instellen dat de uitvoer wordt omgeleid naar een specifieke descriptor voor de duur van het script met behulp van het commando exec:

#!/bin/bash
exec 1>outfile
echo "This is a test of redirecting all output"
echo "from a shell script to another file."
echo "without having to redirect every line"

Laten we het script uitvoeren:

raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat outfile
This is a test of redirecting all output
from a shell script to another file.
without having to redirect every line

Als je kijkt naar het bestand dat in het output redirection commando is opgegeven, dan blijkt dat alles wat de output van het commandoecho naar dat bestand is gegaan.

Het commandoexeckan niet alleen aan het begin van het script worden gebruikt, maar ook op andere plaatsen:

#!/bin/bash
exec 2>myerror
echo "This is the start of the script"
echo "now redirecting all output to another location"
exec 1>myfile
echo "This should go to the myfile file"
echo "and this should go to the myerror file" >&2

Dit is wat er gebeurt na het uitvoeren van het script en het bekijken van de bestanden waarnaar we de uitvoer hebben omgeleid.

raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
This is the start of the script
now redirecting all output to another location
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myerror
and this should go to the myerror file
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
This should go to the myfile file

Eerst stelt het commandoexec de omleiding in van de uitvoer vanSTDERRnaar een bestandmyerror. Dan wordt de uitvoer van verschillende commando’sechoverzonden naarSTDOUT en op het scherm getoond. Daarna stuurt het commandoexec wat naarSTDOUT een bestandmyfile, en tenslotte, gebruiken we het redirection commandoSTDERR in het commandoecho, wat leidt tot het schrijven van de corresponderende regel naar het bestand.myerror.

Als je dit onder de knie hebt, kun je de uitvoer omleiden naar waar je maar wilt. Laten we het nu eens hebben over het omleiden van invoer.

Invoer omleiden in scripts

Om invoer om te leiden, kunt u dezelfde techniek gebruiken die we hebben gebruikt om uitvoer om te leiden. Bijvoorbeeld, met het commando exec kunt u een gegevensbron maken voor STDIN een bestand:

exec 0< myfile

Dit commando vertelt de shell om zijn invoer uit een bestand te halen myfile, niet een gewone STDIN. Laten we input redirection in actie zien:

#!/bin/bash
exec 0< testfile
count=1
while read line
do
echo "Line #$count: $line"
count=$(( $count + 1 ))
done

Dit is wat er op het scherm zal verschijnen na het uitvoeren van het script.

raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line #1: this is the first line
Line #2: this is the second line
Line #3: this is the third line

In een van de vorige artikelen leerde u hoe u een commandoread kunt gebruiken om gebruikersinvoer van het toetsenbord te lezen. Als u invoer omleidt door van de gegevensbron een bestand te maken, dan zal het commandoread, wanneer het probeert gegevens te lezen vanSTDIN, deze lezen van het bestand, en niet van het toetsenbord.

Sommige Linux-beheerders gebruiken deze aanpak om logbestanden te lezen en vervolgens te verwerken.

Eigen uitvoer-omleiding maken

Bij het omleiden van invoer en uitvoer in scripts bent u niet beperkt tot de drie standaard bestandsdescriptors. Zoals gezegd, je kunt tot negen open descriptors hebben. De andere zes, genummerd 3 tot en met 8, kunnen worden gebruikt voor het omleiden van invoer of uitvoer. Elk van hen kan aan een bestand worden toegewezen en in de scriptcode worden gebruikt.

U kunt een descriptor voor data-uitvoer toewijzen met het commando exec:

#!/bin/bash
exec 3>myfile
echo "This should display on the screen"
echo "and this should be stored in the file" >&3
echo "And this should be back on the screen"

Na het uitvoeren van het script zal een deel van de uitvoer naar het scherm gaan, en een deel naar het bestand met de descriptor 3.

raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
This should display on the screen
And this should be back on the screen
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
and this should be stored in the file

Bestandsscriptors maken voor data-invoer

U kunt invoer in een script op dezelfde manier omleiden als uitvoer. Sla STDIN op in een andere descriptor voordat je de invoer omleidt.

Als je klaar bent met het lezen van het bestand, kun je STDIN terugzetten en het zoals gewoonlijk gebruiken:

#!/bin/bash
exec 6<&0
exec 0< myfile
count=1
while read line
do
echo "Line #$count: $line"
count=$(( $count + 1 ))
done
exec 0<&6
read -p "Are you done now? " answer
case $answer in
y) echo "Goodbye";;
n) echo "Sorry, this is the end.";;
esac

Laten we het script eens testen:

raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line #1: First line
Line #2: Second line
Line #3: Third line
Are you done now? y
Goodbye
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line #1: First line
Line #2: Second line
Line #3: Third line
Are you done now? n
Sorry, this is the end.

In dit voorbeeld werd file descriptor 6 gebruikt om een verwijzing naarSTDIN op te slaan. Daarna werd input redirection gedaan, de gegevensbron voor hetSTDINbestand werd. Daarna kwam de invoer voor het commandoreaduit de omgeleideSTDIN, dat wil zeggen, uit het bestand.

Na het lezen van het bestand, keren weSTDINterug naar zijn oorspronkelijke staat door het om te leiden naar een descriptor6. Om te controleren of alles correct werkt, stelt het script de gebruiker een vraag, wacht op invoer via het toetsenbord, en verwerkt wat is ingevoerd.

Bestandsscriptors sluiten

De shell sluit automatisch bestandsscriptors nadat het script is voltooid. In sommige gevallen is het echter nodig om de descriptors handmatig te sluiten voordat het script klaar is met werken. Om het handvat te sluiten, moet het worden omgeleid naar &-. Het ziet er zo uit:

#!/bin/bash
exec 3> myfile
echo "This is a test line of data" >&3
exec 3>&-
echo "This won't work" >&3

Na het uitvoeren van het script krijgen we een foutmelding.

raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
./myscript: line 5: 3: Bad file descriptor

Het punt is dat we hebben geprobeerd toegang te krijgen tot een niet-bestaande bestandsdescriptor.

Wees voorzichtig met het sluiten van bestandsdescriptors in scripts. Als je gegevens naar een bestand stuurt, dan het handvat sluit, en het dan weer opent, zal de shell het bestaande bestand vervangen door het nieuwe. Dat wil zeggen, alles wat eerder naar dit bestand is geschreven, gaat verloren.

Opvragen van informatie over open handles

Om een lijst van alle open descriptors in Linux op te vragen, kunt u het commando lsof gebruiken. Veel distributies, zoals Fedora, hebben het hulpprogramma lsofin /usr/sbin. Dit commando is erg nuttig omdat het informatie toont over ieder handle dat open staat op het systeem. Dit omvat wat geopend is door processen die op de achtergrond draaien en wat geopend is door gebruikers die ingelogd zijn.

Dit commando heeft veel toetsen, laten we eens kijken naar de belangrijkste.

  • -p Hiermee kunt u een ID proces opgeven.
  • -d Hiermee kunt u het nummer opgeven van een descriptor waarover u informatie wilt krijgen.

Om de PID van het huidige proces te achterhalen, kunt u een speciale omgevingsvariabele $$ gebruiken, waarnaar de shell de huidige PID schrijft.

De sleutel wordt -agebruikt om een booleaanse bewerking AND uit te voeren op de resultaten die door gebruik van de andere twee sleutels zijn teruggekomen:

raevskym@DESKTOP-JNF3L6H:~/bash_course$ lsof -a -p $$ -d 0,1,2
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
myscript 24 raevskym 0u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 1u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 2u CHR 4,1 25332 /dev/tty1

Het type bestanden dat hoort bijSTDINSTDOUTenSTDERR — CHR (tekenmodus). Omdat ze allemaal naar een terminal wijzen, komt de bestandsnaam overeen met de apparaatnaam die aan de terminal is toegewezen. Alle drie de standaard bestanden zijn beschikbaar voor lezen en schrijven.

Laten we eens kijken naar het aanroepen van een commandolsofvanuit een script waarin, naast de standaard, ook andere descriptors open staan:

#!/bin/bash
exec 3> myfile1
exec 6> myfile2
exec 7< myfile3
lsof -a -p $$ -d 0,1,2,3,6,7

Dit is wat er gebeurt als je dit script uitvoert.

raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
myscript 24 raevskym 0u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 1u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 2u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 3w REG 0,14 0 13510 ~/bash_course/myfile1
myscript 24 raevskym 6w REG 0,14 0 13229 ~/bash_course/myfile2

Het script heeft twee descriptors geopend voor uitvoer (3en6) en een voor invoer (7). De paden naar de bestanden die worden gebruikt om de descriptors te configureren, worden hier ook getoond.

Output suppression

Soms moet u ervoor zorgen dat de commando’s in het script, die bijvoorbeeld als achtergrondproces kunnen worden uitgevoerd, niets op het scherm tonen. Om dit te doen, kun je de uitvoer omleiden naar /dev/null. Dit is zoiets als een “zwart gat”.

Hier ziet u bijvoorbeeld hoe u foutmeldingen kunt onderdrukken:

ls -al badfile anotherfile 2> /dev/null

Dezelfde aanpak wordt gebruikt als u bijvoorbeeld een bestand moet opruimen zonder het te verwijderen:

cat /dev/null > myfile

Uitkomst

Vandaag heeft u geleerd hoe invoer en uitvoer werken in command-line scripting. U weet nu hoe u met file descriptors om moet gaan, hoe u ze moet maken, bekijken en sluiten, u weet hoe u input, output en error streams moet omleiden. Dit is allemaal erg belangrijk bij het ontwikkelen van bash scripts.

De volgende keer gaan we het hebben over Linux signalen, hoe je daar mee omgaat in scripts, hoe je geplande taken uitvoert, en achtergrond taken.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *