Tips en Trucs 2018

Opdrachten na elkaar en/of simultaan uitvoeren

Als je meer efficiëntie wilt bij het uitvoeren van opdrachten in de terminal, kan je verschillende opdrachten na elkaar en/of simultaan laten uitvoeren.

Na elkaar

Punt komma

Wanneer twee opdrachten gescheiden door een ; worden beide opdrachten na elkaar uitgevoerd.

dany@pindabook:~> ls; sleep 1; date; sleep 1; df
Afbeeldingen  bin  Bureaublad  Documenten  Downloads  Muziek  onedrive  OneDrive  Openbaar  Sjablonen  Video's
vr 29 jun 2018 14:04:46 CEST
Bestandssysteem 1K-blokken Gebruikt Beschikbaar Geb% Aangekoppeld op
devtmpfs           4029160        0     4029160   0% /dev
tmpfs              4037356        0     4037356   0% /dev/shm
tmpfs              4037356     9880     4027476   1% /run
tmpfs              4037356        0     4037356   0% /sys/fs/cgroup
/dev/sda4         82042776  7444840    70387252  10% /
/dev/sda2           262144    57616      204528  22% /boot/efi
/dev/sda7        153906204 25860992   120204192  18% /usr/home/Documents
tmpfs               807468       24      807444   1% /run/user/1000

De sleep 1 opdracht doet niets anders dan 1 seconde wachten, waardoor je zeer goed merkt dat de opdrachten na elkaar worden uitgevoerd.

De logische AND operatie "&&"

Soms mag de tweede opdracht, pas uitgevoerd worden als de eerste opdracht succesvol was.

dany@pindabook:~> mkdir /tmp/data && cd /tmp/data
dany@pindabook:/tmp/data>

Eerst wordt de map data aangemaakt in de /tmp map en pas als de map data succesvol is aangemaakt, wordt deze betreden. Om dit te testen herhalen we de opdracht:

dany@pindabook:/tmp/data> cd
dany@pindabook:~> mkdir /tmp/data && cd /tmp/data
mkdir: kan map ‘/tmp/data’ niet aanmaken: Bestand bestaat al
dany@pindabook:~>

Door het falen van de mkdir opdracht, blijven we in dezelfde map werken. M.a.w. de cd opdracht wordt niet uitgevoerd. De logische AND operatie controleert of de voorgaande opdracht succesvol werd afgewerkt en voert de daaropvolgende opdracht pas uit als dat zo is. Het na elkaar uitvoeren van opdrachten zonder logische AND kan zware gevolgen hebben, een voorbeeld:

dany@pindabook:~> cd /tmp/data
dany@pindabook:/tmp/data> touch 1.txt; touch 2.txt; touch 3.txt
dany@pindabook:/tmp/data> ls
1.txt  2.txt  3.txt
dany@pindabook:/tmp/data> cd /tmp/data1; rm -rf *
-bash: cd: /tmp/data1: Bestand of map bestaat niet
dany@pindabook:/tmp/data> ls
dany@pindabook:/tmp/data>

De opdrachten cd /tmp/data1; rm -rf * werden allebei uitgevoerd met catastrofale gevolgen. Door het mislukken van de cd opdracht, werden alle bestanden in de huidige map gewist, wat niet de bedoeling was. Hetzelfde voorbeeld met de logische AND operatie:

dany@pindabook:/tmp/data> touch 1.txt; touch 2.txt; touch 3.txt
dany@pindabook:/tmp/data> ls
1.txt  2.txt  3.txt
dany@pindabook:/tmp/data> cd /tmp/data1 && rm -rf *
-bash: cd: /tmp/data1: Bestand of map bestaat niet
dany@pindabook:/tmp/data> ls
1.txt  2.txt  3.txt
dany@pindabook:/tmp/data>

De inhoud van de map werd niet verwijderd, omdat de map /tmp/data1 niet bereikbaar was. M.a.w. de rm -rf * opdracht werd niet uitgevoerd omdat de cd /tmp/data1 opdracht mislukt was.

De logische OR operatie "||"

In andere gevallen mag de tweede opdracht enkel uitgevoerd worden als de eerste niet succesvol was. In zo'n gevallen gebruiken we de logische OR operatie.

dany@pindabook:/tmp/data> [ -d /tmp/data1 ] || mkdir /tmp/data1
dany@pindabook:/tmp/data>

We maken de map enkel aan als deze nog niet bestaat. De opdracht [ -d /tmp/data1 ] test of de map /tmp/data1 bestaat, indien dit niet zo is, wordt deze aangemaakt.

Combineren

dany@pindabook:/tmp/data> cat /tmp/data/1.txt > /dev/null && echo "Bestand kan geopend worden" || echo "Bestand kan niet geopend worden"
Bestand kan geopend worden
dany@pindabook:/tmp/data> cat /tmp/data1/1.txt > /dev/null && echo "Bestand kan geopend worden" || echo "Bestand kan niet geopend worden"
cat: /tmp/data1/1.txt: Bestand of map bestaat niet
Bestand kan niet geopend worden

Naargelang de inhoud van een bestand gelezen kan worden of niet, wordt een gepast bericht weergegeven.

Tijd om wat op te ruimen:

dany@pindabook:/tmp/data> [ -d /tmp/data1 ] && rm -r /tmp/data1 || echo "Map bestaat niet"
dany@pindabook:/tmp/data> [ -d /tmp/data1 ] && rm -r /tmp/data1 || echo "Map bestaat niet"
Map bestaat niet

Alles samen:

dany@pindabook:/tmp/data> [[ $PWD = "/tmp/data" ]] && { echo "Eerst verlaten we de map."; cd; } || { echo "Daarna kan die gewist worden."; rm -r /tmp/data/; }
Eerst verlaten we de map.
dany@pindabook:~> [[ $PWD = "/tmp/data" ]] && { echo "Eerst verlaten we de map."; cd; } || { echo "Daarna kan die gewist worden."; rm -r /tmp/data/; }
Daarna kan die gewist worden.
dany@pindabook:~> ls /tmp/data*
ls: kan geen toegang krijgen tot '/tmp/data*': Bestand of map bestaat niet

Let bij deze opdrachten dat opdrachten die omgeven zijn door accolades een groep vormen en dus steeds samen (na elkaar) uitgevoerd worden. De spaties na de eerste accolade en de kommapunt met spatie voor de afsluitende accolade zijn verplicht.

Simultaan

Om verschillende opdrachten tegelijkertijd uit te voeren, moet je ze in de achtergrond opstarten. Of m.a.w. de opdrachten parallel starten.

Opdrachten in de achtergrond opstarten

dany@pindabook:~> sleep 60 &
[1] 3699
dany@pindabook:~> sleep 120 &
[2] 3725
dany@pindabook:~> sleep 90 &
[3] 3726
dany@pindabook:~> jobs
[1]   Wordt uitgevoerd        sleep 60 &
[2]-  Wordt uitgevoerd        sleep 120 &
[3]+  Wordt uitgevoerd        sleep 90 &

De ampersand na de sleep opdrachten zorgt ervoor dat deze in de achtergrond opgestart worden. Dat merk je meteen doordat de prompt onmiddellijk terug verschijnt waardoor je een tweede en zelfs een derde (enz.) opdracht in de achtergrond kunt opstarten. Met de jobs opdracht kan je opvragen welke opdrachten nog op de achtergrond werken en welke reeds afgewerkt zijn.

dany@pindabook:~> jobs
[1]   Klaar                   sleep 60
[2]-  Klaar                   sleep 120
[3]+  Klaar                   sleep 90

Ook in scripts kan je dit gebruiken om bijvoorbeeld functies parallel te starten.

#!/bin/bash
# Onze op de achtergrond werkende functie
achtergrond(){
  echo "Doorloop $1..."
  sleep 1
}
# 5 keer uitvoeren
for i in {1..5}
do
	achtergrond $i & # Voer een functie in de achtergrond uit
done
 
## Alle achtergrondfuncties worden uitgevoerd 
## met wait wachten we tot alle achtergrondfuncties voltooid zijn
## daarna krijg je het bericht Klaar
wait 
echo "Klaar"

Bij het uitvoeren merk je enkel een vertraging na het starten van de vijf parallelle functies.

dany@pindabook:~> chmod +x achtergrond.sh 
dany@pindabook:~> ./achtergrond.sh 
Doorloop 1...
Doorloop 2...
Doorloop 3...
Doorloop 5...
Doorloop 4...
Klaar

Om verschillende bestanden in een tekstbestand (lijst.txt) parallel met wget te downloaden. Een voorbeeldlijst:

https://images.wallpaperscraft.com/image/vase_letter_flowers_123287_1920x1080.jpg
https://images.wallpaperscraft.com/image/jackal_grass_dark_123286_1920x1080.jpg
https://images.wallpaperscraft.com/image/pier_sunset_lake_123285_1920x1080.jpg
https://images.wallpaperscraft.com/image/drops_glass_surface_123284_1920x1080.jpg
https://images.wallpaperscraft.com/image/mountains_stones_tourist_123283_1920x1080.jpg
https://images.wallpaperscraft.com/image/film_negative_glare_123282_1920x1080.jpg
https://images.wallpaperscraft.com/image/woman_magician_magic_123280_1920x1080.jpg
https://images.wallpaperscraft.com/image/poppies_field_sunset_123279_1920x1080.jpg
https://images.wallpaperscraft.com/image/monkey_primate_branch_123278_1920x1080.jpg
https://images.wallpaperscraft.com/image/headphones_audio_style_123277_1920x1080.jpg
https://images.wallpaperscraft.com/image/trees_bw_branches_123276_1920x1080.jpg
https://images.wallpaperscraft.com/image/man_forest_smoke_123275_1920x1080.jpg

Om ook het laatste bestand te downloaden, moet deze lijst eindigen met een lege regel. En het parallel downloadscript:

#!/bin/bash
# De download functie
achtergrond(){
  wget -q "$1"
}
 
while IFS= read -r url
do
        achtergrond "$url" &
done < lijst.txt
 
wait
echo "Alle bestanden zijn binnen."

Bij het uitvoeren van het script, krijgen we:

dany@pindabook:~> chmod +x wget_parallel.sh 
dany@pindabook:~> ./wget_parallel.sh 
Alle bestanden zijn binnen.
dany@pindabook:~> ls -l *.jpg
-rw-r--r-- 1 dany users  953372 28 jun 15:05 drops_glass_surface_123284_1920x1080.jpg
-rw-r--r-- 1 dany users  901171 28 jun 14:45 film_negative_glare_123282_1920x1080.jpg
-rw-r--r-- 1 dany users  770111 28 jun 14:30 headphones_audio_style_123277_1920x1080.jpg
-rw-r--r-- 1 dany users  404783 28 jun 15:10 jackal_grass_dark_123286_1920x1080.jpg
-rw-r--r-- 1 dany users  828167 28 jun 14:25 man_forest_smoke_123275_1920x1080.jpg
-rw-r--r-- 1 dany users  816218 28 jun 14:35 monkey_primate_branch_123278_1920x1080.jpg
-rw-r--r-- 1 dany users  974004 28 jun 14:50 mountains_stones_tourist_123283_1920x1080.jpg
-rw-r--r-- 1 dany users  880438 28 jun 15:05 pier_sunset_lake_123285_1920x1080.jpg
-rw-r--r-- 1 dany users 1182467 28 jun 14:40 poppies_field_sunset_123279_1920x1080.jpg
-rw-r--r-- 1 dany users 1437145 28 jun 14:30 trees_bw_branches_123276_1920x1080.jpg
-rw-r--r-- 1 dany users  815417 28 jun 15:10 vase_letter_flowers_123287_1920x1080.jpg
-rw-r--r-- 1 dany users  663532 28 jun 14:45 woman_magician_magic_123280_1920x1080.jpg

GNU parallel

Hoewel de ingebouwde bash mogelijkheden om functies en opdrachten op de achtergrond uit te voeren voor de meeste toepassingen zullen volstaan, zullen sommige ontwikkelaars tegen beperkingen oplopen. Voor wie meer wil, is er GNU parallel. De installatie van GNU parallel verloopt in openSUSE Leap 15 als volgt:

dany@pindabook:~> parallel
If 'parallel' is not a typo you can use command-not-found to lookup the package that contains it, like this:
    cnf parallel
dany@pindabook:~> cnf parallel
                               
Het programma 'parallel' kan gevonden worden in de volgende pakketten:
  * gnu_parallel [ pad: /usr/bin/parallel, installatiebron: zypp (repo-oss) ]
  * moreutils-parallel [ pad: /usr/bin/parallel, installatiebron: zypp (repo-oss) ]

Try installing with:
    sudo zypper install 

dany@pindabook:~> sudo zypper install gnu_parallel
[sudo] wachtwoord voor root: 
Gegevens van opslagruimte laden...
Lezen van geïnstalleerde pakketten...
Pakketafhankelijkheden oplossen...

Het volgende NIEUWE pakket zal worden geïnstalleerd:
  gnu_parallel

1 nieuw te installeren pakket.
Totale downloadgrootte: 282,4 KiB. Reeds in de cache: 0 B. Na de bewerking zal aanvullend 650,8 KiB worden gebruikt.
Doorgaan? [j/n/...? alle opties tonen] (j): 
pakket gnu_parallel-20180422-lp150.1.1.noarch wordt opgehaald                                  (1/1), 282,4 KiB (650,8 KiB uitgepakt)
Ophalen: gnu_parallel-20180422-lp150.1.1.noarch.rpm .........................................................................[gereed]
Controleren op conflicten tussen bestanden: .................................................................................[gereed]
(1/1) Installeren van: gnu_parallel-20180422-lp150.1.1.noarch ...............................................................[gereed]

De volgende opdracht zondert de bestandsnamen uit de URL's in de lijst af en comprimeert de zo gevonden JPG-bestanden met gzip:

dany@pindabook:~> cat lijst.txt | sed 's/.*\///' | parallel gzip --best
dany@pindabook:~> ls -l *.jpg.gz
-rw-r--r-- 1 dany users  953255 28 jun 15:05 drops_glass_surface_123284_1920x1080.jpg.gz
-rw-r--r-- 1 dany users  901021 28 jun 14:45 film_negative_glare_123282_1920x1080.jpg.gz
-rw-r--r-- 1 dany users  769840 28 jun 14:30 headphones_audio_style_123277_1920x1080.jpg.gz
-rw-r--r-- 1 dany users  403365 28 jun 15:10 jackal_grass_dark_123286_1920x1080.jpg.gz
-rw-r--r-- 1 dany users  828156 28 jun 14:25 man_forest_smoke_123275_1920x1080.jpg.gz
-rw-r--r-- 1 dany users  816199 28 jun 14:35 monkey_primate_branch_123278_1920x1080.jpg.gz
-rw-r--r-- 1 dany users  974010 28 jun 14:50 mountains_stones_tourist_123283_1920x1080.jpg.gz
-rw-r--r-- 1 dany users  880040 28 jun 15:05 pier_sunset_lake_123285_1920x1080.jpg.gz
-rw-r--r-- 1 dany users 1181100 28 jun 14:40 poppies_field_sunset_123279_1920x1080.jpg.gz
-rw-r--r-- 1 dany users 1423849 28 jun 14:30 trees_bw_branches_123276_1920x1080.jpg.gz
-rw-r--r-- 1 dany users  814830 28 jun 15:10 vase_letter_flowers_123287_1920x1080.jpg.gz
-rw-r--r-- 1 dany users  663516 28 jun 14:45 woman_magician_magic_123280_1920x1080.jpg.gz

De oplettende lezer merkt al vlug dat reeds gecomprimeerde jpg-bestanden nogmaals comprimeren weinig zin heeft. De volgende opdracht verwijderd alle gedownloade gecomprimeerde bestanden op de lijst:

dany@pindabook:~> for f in `cat lijst.txt`; do rm `basename ${f}.gz`; done;

Het parallel downloaden van de bestanden opgesomd in lijst.txt met de parallel opdracht:

dany@pindabook:~> cat lijst.txt | parallel -j 4 wget -q {}
dany@pindabook:~> ls -l *.jpg
-rw-r--r-- 1 dany users  953372 28 jun 15:05 drops_glass_surface_123284_1920x1080.jpg
-rw-r--r-- 1 dany users  901171 28 jun 14:45 film_negative_glare_123282_1920x1080.jpg
-rw-r--r-- 1 dany users  770111 28 jun 14:30 headphones_audio_style_123277_1920x1080.jpg
-rw-r--r-- 1 dany users  404783 28 jun 15:10 jackal_grass_dark_123286_1920x1080.jpg
-rw-r--r-- 1 dany users  828167 28 jun 14:25 man_forest_smoke_123275_1920x1080.jpg
-rw-r--r-- 1 dany users  816218 28 jun 14:35 monkey_primate_branch_123278_1920x1080.jpg
-rw-r--r-- 1 dany users  974004 28 jun 14:50 mountains_stones_tourist_123283_1920x1080.jpg
-rw-r--r-- 1 dany users  880438 28 jun 15:05 pier_sunset_lake_123285_1920x1080.jpg
-rw-r--r-- 1 dany users 1182467 28 jun 14:40 poppies_field_sunset_123279_1920x1080.jpg
-rw-r--r-- 1 dany users 1437145 28 jun 14:30 trees_bw_branches_123276_1920x1080.jpg
-rw-r--r-- 1 dany users  815417 28 jun 15:10 vase_letter_flowers_123287_1920x1080.jpg
-rw-r--r-- 1 dany users  663532 28 jun 14:45 woman_magician_magic_123280_1920x1080.jpg

Of met:

dany@pindabook:~> for f in `cat lijst.txt`; do rm `basename ${f}`; done;
dany@pindabook:~> parallel -j 4 wget -q {} < lijst.txt
dany@pindabook:~> ls -l *.jpg
-rw-r--r-- 1 dany users  953372 28 jun 15:05 drops_glass_surface_123284_1920x1080.jpg
-rw-r--r-- 1 dany users  901171 28 jun 14:45 film_negative_glare_123282_1920x1080.jpg
-rw-r--r-- 1 dany users  770111 28 jun 14:30 headphones_audio_style_123277_1920x1080.jpg
-rw-r--r-- 1 dany users  404783 28 jun 15:10 jackal_grass_dark_123286_1920x1080.jpg
-rw-r--r-- 1 dany users  828167 28 jun 14:25 man_forest_smoke_123275_1920x1080.jpg
-rw-r--r-- 1 dany users  816218 28 jun 14:35 monkey_primate_branch_123278_1920x1080.jpg
-rw-r--r-- 1 dany users  974004 28 jun 14:50 mountains_stones_tourist_123283_1920x1080.jpg
-rw-r--r-- 1 dany users  880438 28 jun 15:05 pier_sunset_lake_123285_1920x1080.jpg
-rw-r--r-- 1 dany users 1182467 28 jun 14:40 poppies_field_sunset_123279_1920x1080.jpg
-rw-r--r-- 1 dany users 1437145 28 jun 14:30 trees_bw_branches_123276_1920x1080.jpg
-rw-r--r-- 1 dany users  815417 28 jun 15:10 vase_letter_flowers_123287_1920x1080.jpg
-rw-r--r-- 1 dany users  663532 28 jun 14:45 woman_magician_magic_123280_1920x1080.jpg

Dit bewijst nogmaals dat wie in een terminal werkt, efficiënt en nauwkeurig werkt. De mogelijkheden van terminalopdrachten kennen geen grenzen, ons toepassingsvermogen misschien wel.

parallel