De read opdracht is een ingebouwde functie van de Bash-shell die gebruikersinvoer vastlegt en toewijst aan variabelen, waardoor het essentieel is voor interactieve scripts.
Veelvoorkomende gebruikssituaties zijn onder meer het vragen om gebruikersnamen en wachtwoorden, het regel voor regel verwerken van CSV-bestanden, het implementeren van bevestigingsdialoogvensters en het bouwen van menugestuurde interfaces.
Aan het einde van deze tip ben je in staat om enkelvoudige en meervoudige invoeren vast te leggen, gevoelige gegevens veilig te verwerken, tijdslimieten te implementeren, bestanden efficiënt te verwerken en veelvoorkomende valkuilen op te lossen.
Als je nog niet bekend bent met read, kun je het zien als een pauzeknop die wacht tot iemand je script beantwoordt voordat het verdergaat.
De read opdracht legt een regel invoer vast vanuit de standaardinvoer (stdin) en wijst deze toe aan één of meer variabelen.
Standaard wacht read op gebruikersinvoer en slaat deze op in een variabele.
In de eenvoudigste vorm wacht read tot iemand een antwoord typt voordat het script verdergaat.
Het volgende script bestaat uit het absolute minimum dat nodig is om één antwoord te verzamelen en onmiddellijk te reageren.
Het script read.sh maken we met de nano teksteditor aan met de volgende opdracht:
dany@pindabook:~$ nano read.sh
En bevat het volgende script:
GNU nano 8.4 read.sh * #!/bin/bash
echo "Wat is uw naam?"
read user_name
echo "Hallo, $user_name!"
^G Hulp ^O Opslaan ^F Zoeken ^K Knippen ^T Opdracht ^C Positie M-U Terugdraaien
^X Afsluiten ^R Inlezen ^\ Vervangen ^U Plakken ^J Uitvullen ^/ Naar regel M-E Herdoen
Sla het script op de sneltoets Ctrl+s en verlaat nano met de sneltoets Ctrl+x. Voer het script uit met:
dany@pindabook:~$ bash read.sh
Wat is uw naam?
John
Hallo, John!
Het script bestaat uit drie stappen:
read opdracht registreert de invoer en slaat deze op in de variabele user_name.In het vervolg van deze tip passen we telkens het script read.sh aan met nano, zoals hierboven uitgelegd.
Wanneer je read gebruikt zonder een variabelenaam op te geven, slaat de opdracht de invoer automatisch op in de ingebouwde variabele $REPLY:
#!/bin/bash
read
echo "Uw antwoord: $REPLY"
Voer het aangepaste script uit:
dany@pindabook:~$ bash read.sh
Mijn invoer
Uw antwoord: Mijn invoer
Dit gedrag is handig in snelle scripts of wanneer je een consistente variabelenaam wilt gebruiken in verschillende delen van het script.
Vergeet niet om REPLY te verwijderen (unset REPLY) of te overschrijven nadat je ermee klaar bent, zodat de waarde niet in latere opdrachten terechtkomt.
Naast het vastleggen van afzonderlijke waarden kan de read opdracht meerdere invoeren opslaan door meerdere variabelenamen op te geven.
Wanneer een gebruiker waarden opgeeft die door spaties zijn gescheiden, wijst read elke waarde toe aan een overeenkomstige variabele.
#!/bin/bash
echo "Geef jouw voornaam en naam in:"
read first_name last_name
echo "Voornaam: $first_name"
echo "Naam: $last_name"
dany@pindabook:~$ bash read.sh
Geef jouw voornaam en naam in:
John Doe
Voornaam: John
Naam: Doe
Zo werkt het:
read de eerste waarde (John) op in first_name en plaatst de tweede waarde (Doe) in last_name.last_name) de resterende waarden op.Wanneer het aantal invoervelden varieert of je meerdere waarden efficiënt moet verwerken, kun je met de optie -a de invoer rechtstreeks in een array opslaan.
#!/bin/bash
read -a colors -p "Geef jouw favoriete kleuren in: "
echo "Eerste kleur: ${colors[0]}"
echo "Tweede kleur: ${colors[1]}"
echo "Alle kleuren: ${colors[@]}"
dany@pindabook:~$ bash read.sh
Geef jouw favoriete kleuren in: Rood Groen Blauw
Eerste kleur: Rood
Tweede kleur: Groen
Alle kleuren: Rood Groen Blauw
Hoe de optie -a arrays bouwt:
-a optie geeft read de opdracht om elk woord dat door een spatie wordt gescheiden op te slaan als een array-element.-p geeft een prompt (melding) weer zodat gebruikers weten welke gegevens ze moeten invoeren voordat de opdracht op invoer wacht.${array[index]}.${array[@]} om alle array-elementen tegelijk te benaderen.Arrays zijn vooral handig bij het verwerken van opdracht resultaten of het bouwen van dynamische configuraties.
Door een herestring (<<<) te gebruiken, blijft de array in de huidige shell item per item gelezen worden, zodat deze gevuld blijft voor de volgende lus.
Als je in plaats daarvan een pipe (invoeromleiding) naar de opdracht gebruikt, wordt de lus in een subshell uitgevoerd en wordt de array verwijderd zodra de subshell wordt afgesloten.
Gebruik dus een herestring of invoeromleiding wanneer je de gegevens opnieuw wilt gebruiken.
#!/bin/bash
read -a servers <<< "pindanet.be google.be debian.be"
for server in "${servers[@]}"; do
echo "Pinging $server..."
ping -c 1 "$server" > /dev/null 2>&1 && echo "$server is online"
done
dany@pindabook:~$ bash read.sh
Pinging pindanet.be...
pindanet.be is online
Pinging google.be...
google.be is online
Pinging debian.be...
De laatste server (debian.be) is niet online, want deze bestaat niet.
Een vereenvoudigd voorbeeld:
#!/bin/bash
read -r first last <<< "John Doe"
echo "Voornaam: $first, Naam: $last"
dany@pindabook:~$ bash read.sh
Voornaam: John, Naam: Doe
Deze techniek is vooral handig in combinatie met opdrachtvervanging om opdracht resultaten direct te verwerken:
#!/bin/bash
IFS="@" read -r user host <<< "$(whoami)@$(hostname)"
echo "Actieve gebruiker: $user op host: $host"
dany@pindabook:~$ bash read.sh
Actieve gebruiker: dany op host: pindabook
Door IFS in te stellen op @ voor de duur van opdracht, wordt de uitvoer netjes opgesplitst in de velden user en hostname, in plaats van de hele tekenreeks als één waarde te behandelen.
Standaard splitst read invoer op witruimte, zoals spaties, tabs en nieuwe regels, omdat de Internal Field Separator (IFS) deze waarden bevat.
Bij gestructureerde gegevens, zoals CSV-bestanden of andere gescheiden formaten, heb je vaak een ander scheidingsteken nodig.
Door IFS aan te passen, verander je hoe read tekst splitst.
De IFS-variabele bepaalt dus hoe woorden binnen een regel worden gesplitst.
Door deze in te stellen op een komma vóór read, wordt de opdracht geïnstrueerd om op komma's te splitsen in plaats van op witruimte:
#!/bin/bash
IFS="," read -r name age city <<< "John Doe,30,Brugge"
echo "Naam: $name"
echo "Leeftijd: $age"
echo "Woonplaats: $city"
dany@pindabook:~$ bash read.sh
Naam: John Doe
Leeftijd: 30
Woonplaats: Brugge
Hoe IFS=”,” CSV-velden splitst:
IFS="," stelt het scheidingsteken in op een komma.John Doe,30,Brugge wordt opgesplitst in drie variabelen: name, age en city.-r voorkomt dat backslashes (\) als escape-tekens fungeren.Een meer praktische implementatie zou zijn om een CSV-bestand regel voor regel te verwerken.
Om dit te demonstreren, maken we een CSV-bestand met nano aan:
dany@pindabook:~$ nano users.csv
Met de volgende CSV-regels:
John Doe,30,Brugge
Clara Doe,29,Blankenberge
Stefaan Doe,24,Gent
Het volgende script verwerkt gestructureerde gegevens in het pas aangemaakte CSV-bestand op efficiënte wijze, waardoor het nuttig is voor gegevensmigratie, rapportage en automatisering.
#!/bin/bash
while IFS="," read -r name age city; do
echo "Naam: $name, Leeftijd: $age, Woonplaats: $city"
done < users.csv
dany@pindabook:~$ bash read.sh
Naam: John Doe, Leeftijd: 30, Woonplaats: Brugge
Naam: Clara Doe, Leeftijd: 29, Woonplaats: Blankenberge
Naam: Stefaan Doe, Leeftijd: 24, Woonplaats: Gent
Terwijl IFS bepaalt hoe woorden binnen een regel worden gesplitst, verandert de -d optie wat het einde van een regel markeert.
Standaard stopt read bij een nieuwe regel, maar je kunt een ander teken specificeren:
#!/bin/bash
read -d ";" data <<< "status=ready;pending"
echo "Lees tot aan de puntkomma: $data"
dany@pindabook:~$ bash read.sh
Lees tot aan de puntkomma: status=ready
Dit is handig bij het verwerken van gegevens die niet-standaard regeleinden gebruiken, zoals Windows-geformatteerde bestanden of aangepaste gegevensstromen. De herestring houdt de tekenreeks vast en de opdracht gebruikt de puntkomma als scheidingsteken, zodat deze niet wordt opgenomen in de opgeslagen waarde.
#!/bin/bash
read -s -p "Voer uw wachtwoord in: " password
printf "\nWachtwoord ontvangen.\n"
dany@pindabook:~$ bash read.sh
Voer uw wachtwoord in:
Wachtwoord ontvangen.
Bij het verwerken van gevoelige gegevens zoals wachtwoorden of API-sleutels is het niet wenselijk om gebruikersinvoer op de terminal weer te geven.
De -s optie zorgt ervoor dat de invoer verborgen blijft.
#!/bin/bash
read -s -p "Voer de MySQL gebruiker in: " mysql_user
printf "\n"
read -s -p "Voer het MySQL wachtwoord in: " mysql_pass
printf "\n"
mysql --defaults-extra-file=<(printf "[client]\nuser=%s\npassword=%s\n" "$mysql_user" "$mysql_pass") -e "SHOW DATABASES;"
unset mysql_user
unset mysql_pass
dany@pindabook:~$ bash read.sh
Voer de MySQL gebruiker in:
Voer het MySQL wachtwoord in:
+--------------------+
| Database |
+--------------------+
| information_schema |
| pindanet_db |
+--------------------+
Afgeschermde wachtwoordinvoer:
-s verbergt de wachtwoordinvoer, zodat de terminal de tekens nooit weergeeft.-p geeft een gebruiksvriendelijke promptmelding.Door een tijdelijk bestand met clientgegevens te gebruiken, blijft het wachtwoord buiten de proceslijst. Wis de variabele daarna altijd om de blootstelling in het geheugen te verminderen.
Voor eenvoudige ja/nee-bevestigingen of menuselecties is het efficiënter om één enkel teken te lezen dan te wachten op volledige tekstinvoer.
De -n optie beperkt het aantal vastgelegde tekens.
#!/bin/bash
read -n 1 -p "Druk op J om door te gaan: " key
printf "\nJe hebt het volgende ingedrukt: %s\n" "$key"
dany@pindabook:~$ bash read.sh
Druk op J om door te gaan: n
Je hebt het volgende ingedrukt: n
Eén teken invoeren:
-n 1 zorgt ervoor dat slechts één teken wordt gelezen.-p geeft een bericht weer voordat de invoer wordt vastgelegd.Scripts die snelle bevestigingen vereisen, gebruiken deze methode:
#!/bin/bash
read -n 1 -p "Nu opnieuw opstarten? (J/N): " confirm
printf "\n"
[[ $confirm == [Jj] ]] && sudo systemctl reboot
dany@pindabook:~$ bash read.sh
Nu opnieuw opstarten? (J/N): n
dany@pindabook:~$ bash read.sh
Nu opnieuw opstarten? (J/N): j
[sudo] wachtwoord voor dany:
sudo: een wachtwoord is verplicht
Je kunt het uitvoeren van een herstart (en een script) afbreken met de sneltoets Ctrl+c. Deze techniek verbetert de gebruikerservaring door onnodige toetsaanslagen te verminderen.
-NDe -N optie (hoofdletters) leest precies het opgegeven aantal tekens en negeert alle scheidingstekens, inclusief nieuwe regels:
#!/bin/bash
read -N 6 -p "Voer de PIN-code (precies 6 tekens) in: " code
printf "\nIngevoerde code: %s\n" "$code"
dany@pindabook:~$ bash read.sh
Voer de PIN-code (precies 6 tekens) in: aqwzsx
Ingevoerde code: aqwzsx
In tegenstelling tot -n wordt de opdracht bij gebruik van -N niet beëindigd door op Enter te drukken.
De opdracht wacht tot je precies zes tekens hebt ingevoerd.
Dit is handig voor codes met een vaste lengte, pincodes of het valideren van opgemaakte invoer.
In geautomatiseerde of onbemande omgevingen zijn er scenario's waarin een script moet doorgaan als de gebruiker niet binnen een bepaalde tijd invoer ingeeft.
Met de -t optie kan een time-out worden ingesteld om te voorkomen dat er oneindig lang wordt gewacht.
#!/bin/bash
echo "1 Opslaan en afsluiten."
echo "2 Niet opslaan en afsluiten."
echo "3 Verder werken."
read -t 5 -n 1 -p "Voer uw keuze in (standaard is ‘Verder werken’): " choice
printf "\n"
choice=${choice:-"3"}
echo "Uw keuze: $choice"
dany@pindabook:~$ bash read.sh
1 Opslaan en afsluiten.
2 Niet opslaan en afsluiten.
3 Verder werken.
Voer uw keuze in (standaard is ‘Verder werken’): 1
Uw keuze: 1
dany@pindabook:~$ bash read.sh
1 Opslaan en afsluiten.
2 Niet opslaan en afsluiten.
3 Verder werken.
Voer uw keuze in (standaard is ‘Verder werken’):
Uw keuze: 3
Wat gebeurt er?
-t 5 stelt een tijdslimiet van 5 seconden in.3 Verder werken).Bij onbemande (automatische) scripts gebruiken vaak tijdslimieten:
#!/bin/bash
echo -n "Druk op een willekeurige toets om door te gaan, of wacht 5 seconden... "
read -t 5 -n 1 key || printf "\nTijdslimiet verstreken, doorgaan..."
printf "\n"
dany@pindabook:~$ bash read.sh
Druk op een willekeurige toets om door te gaan, of wacht 5 seconden... d
dany@pindabook:~$ bash read.sh
Druk op een willekeurige toets om door te gaan, of wacht 5 seconden...
Tijdslimiet verstreken, doorgaan...
De read opdracht retourneert verschillende exitcodes, afhankelijk van of de bewerking slaagt of mislukt.
Een goede foutafhandeling zorgt ervoor dat scripts in alle scenario's correct werken.
#!/bin/bash
echo "1 Opslaan en afsluiten."
echo "2 Niet opslaan en afsluiten."
echo "3 Verder werken."
if read -t 5 -p "Voer uw keuze in: " choice; then
echo "U hebt gekozen voor: $choice"
else
echo "Geen invoer ontvangen of tijdslimiet overschreden."
fi
dany@pindabook:~$ bash read.sh
1 Opslaan en afsluiten.
2 Niet opslaan en afsluiten.
3 Verder werken.
Voer uw keuze in: 1
U hebt gekozen voor: 1
dany@pindabook:~$ bash read.sh
1 Opslaan en afsluiten.
2 Niet opslaan en afsluiten.
3 Verder werken.
Voer uw keuze in: Geen invoer ontvangen of tijdslimiet overschreden.
Exit waarden:
0: Invoer succesvol gelezenGroter dan 0: Er is een fout opgetreden (time-out, EOF, ongeldige variabelenaam, enz.)Deze structuur is essentieel voor het bouwen van robuuste scripts die op een elegante manier omgaan met gebruikersinteractie, vooral in combinatie met time-outs of bij het lezen van pipes die onverwachts kunnen worden gesloten.
Naast interactieve gebruikersinvoer gebruiken scripters vaak de read opdracht in een lus om bestanden regel voor regel te verwerken.
Deze aanpak is essentieel voor automatiseringstaken waarbij configuratiebestanden moeten worden geanalyseerd, logbestanden moeten worden verwerkt of batchgegevens moeten worden afgehandeld.
#!/bin/bash
while IFS= read -r line; do
echo "Verwerken van: $line"
done < users.csv
dany@pindabook:~$ bash read.sh
Verwerken van: John Doe,30,Brugge
Verwerken van: Clara Doe,29,Blankenberge
Verwerken van: Stefaan Doe,24,Gent
De werking van de lus:
while-lus doorloopt elke regel in users.csv.IFS= read -r line legt de inhoud van elke regel vast zonder spaties weg te laten of backslashes te interpreteren.Het automatiseren van logbestandsverwerking is een ander praktisch scenario waarin read van onschatbare waarde blijkt te zijn.
De onderstaande lus volgt de logboek vermeldingen van de journalctrl opdracht continu, zodat nieuwe vermeldingen jouw automatisering onmiddellijk activeren:
#!/bin/bash
while IFS= read -r log_entry; do
echo "Zoeken in: $log_entry"
[[ $log_entry == *"ERROR"* ]] && echo "Waarschuwing: Er is een fout gevonden!"
done < <(sudo journalctl -fqn0)
dany@pindabook:~$ bash read.sh
[sudo] wachtwoord voor dany:
^C
Daar dit script blijft luisteren naar gegevens van de journalctl opdracht, moet je dit script afbreken met de Ctrl+c sneltoets.
Door een lege IFS-waarde en de -r optie te gebruiken, blijft elke logboekvermelding precies zoals geschreven, terwijl procesvervanging de lus blijft voeden.
Om de logboekanalyse verder te verfijnen, kun je read combineren met de grep opdracht om specifieke patronen te filteren voordat elke regel wordt verwerkt.
Zelfs ervaren gebruikers ondervinden soms problemen bij het werken met de read opdracht.
In de volgende paragrafen worden enkele problemen met foutmeldingen en oplossingen behandeld.
#!/bin/bash
read -t 10 -s -p "Voer uw wachtwoord in: " password
printf "\nWachtwoord ontvangen.\n"
Als je bovenstaande script uitvoert met bash of dash krijg je het volgende:
dany@pindabook:~$ bash read.sh
Voer uw wachtwoord in:
Wachtwoord ontvangen.
dany@pindabook:~$ dash read.sh
read.sh: 2: read: Illegal option -t
Wachtwoord ontvangen.
Het script wordt uitgevoerd in een POSIX-shell die geen Bash-specifieke opties ondersteunt. Los dit op door ervoor te zorgen dat het script met Bash wordt uitgevoerd (eerste regel). Je kunt ook controleren welke shell je script uitvoert (laatste regel).
#!/bin/bash
# Gebruik /bin/bash, niet /bin/sh, voor Bash-functies
echo $0
echo $BASH_VERSION
Scripts worden met de chmod opdracht bijna altijd uitvoerbaar gemaakt.
Daardoor kan je een script starten in de standaard shell (vb: bash).
Met de #!/bin/bash als eerste regel bepaal je zelf waarmee het script wordt uitgevoerd.
dany@pindabook:~$ chmod +x read.sh
dany@pindabook:~$ ./read.sh
./read.sh
5.2.37(1)-release
dany@pindabook:~$ bash read.sh
read.sh
5.2.37(1)-release
dany@pindabook:~$ dash read.sh
read.sh
Als de variabele $BASH_VERSION leeg is, wordt Bash niet gebruikt.
readEen veelgemaakte fout is het doorsturen (pipen) van gegevens naar read en vervolgens ontdekken dat de variabele leeg is:
#!/bin/bash
# Dit werkt NIET zoals verwacht.
echo "Hallo" | read myvar
echo "Waarde: $myvar"
# Correct: gebruik herestring.
read myvar <<< "Hallo"
echo "Waarde: $myvar"
dany@pindabook:~$ bash read.sh
Waarde:
Waarde: Hallo
De variabele in het eerste deel is leeg omdat de pipe een subshell voor de read opdracht aanmaakt en de variabele wordt verwijderd wanneer de subshell wordt afgesloten.
In het tweede deel corrigeren we dit door een herestring of procesvervanging te gebruiken.
Als voor- of achterliggende spaties uit de invoer verdwijnen, worden ze verwijderd door de standaard IFS-waarde (spatie, tab, nieuwe regel).
Stel IFS in op een lege waarde en gebruik de -r optie om de oorspronkelijke invoer te behouden:
#!/bin/bash
# Zonder IFS= worden voorloopspaties verwijderd.
read line <<< " Hallo wereld "
echo "[$line]"
# Met IFS= blijven witruimtes behouden.
IFS= read -r line <<< " Hallo wereld "
echo "[$line]"
dany@pindabook:~$ bash read.sh
[Hallo wereld]
[ Hallo wereld ]
Wanneer je later de variabele gebruikt, zet je deze altijd tussen dubbele aanhalingstekens (bijvoorbeeld "$line") om woordafbreking te voorkomen en de exacte inhoud te behouden.
Zonder de -r optie interpreteert read backslashes (\) als escape-tekens:
#!/bin/bash
# Zonder -r worden backslashes geïnterpreteerd.
read path <<< "C:\Users\test"
echo "$path"
# Met -r blijven backslashes behouden.
read -r path <<< "C:\Users\test"
echo "$path"
dany@pindabook:~$ bash read.sh
C:Userstest
C:\Users\test
Gebruik altijd -r, tenzij je specifiek backslash-interpretatie nodig hebt.
Wanneer je read gebruikt in een while-lus die ook leest vanuit stdin, kan de lus onverwacht worden afgesloten of invoer verwerken die bedoeld is voor de gebruiker (toetsenbord invoer).
Gebruik een andere bestandsdescriptor om bestandsinvoer te scheiden van gebruikersinvoer:
#!/bin/bash
# Lees bestand op fd 3, houd stdin vrij voor gebruikersinvoer.
while IFS= read -r line <&3; do
echo "Verwerken: $line"
read -n 1 -p "Doorgaan? (j/n): " answer
printf "\n"
[[ $answer != "j" ]] && break
done 3< users.csv
dany@pindabook:~$ bash read.sh
Verwerken: John Doe,30,Brugge
Doorgaan? (j/n): j
Verwerken: Clara Doe,29,Blankenberge
Doorgaan? (j/n): n
Deze techniek bewaart bestandsgegevens op bestanddescriptor 3, terwijl stdin beschikbaar blijft voor interactieve toetsenbord invoer.

Dit is vandaag eenvoudig. Enkel de twee aangemaakte bestanden verwijderen en klaar:
dany@pindabook:~$ rm read.sh users.csv