Batch processing op de commandline – Makkelijker dan je denkt!

About

Batch processing op de commandline – Makkelijker dan je denkt!

Commandline tools zijn erg handig om taken te automatiseren. Ken je het commando om bijvoorbeeld één fotobestand te verkleinen? Dan is het niet moeilijk om hetzelfde te doen met pakweg 100 of 1000 bestanden. We tonen hier kort enkele tools die je daarbij helpen.

Auteur: Filip Vervloesem

In ons voorbeeld gebruiken we het commando convert uit het pakket imagemagick. Uiteraard kan je dezelfde technieken gebruiken met andere tools of zelfs andere bestandstypes. We beginnen met een map van een duizendtal jpeg-foto’s die in totaal 8GB groot is. We willen de foto’s delen met vrienden, maar vinden 8GB toch iets té groot om te uploaden naar de cloud. De foto’s zijn immers 10 megapixels groot:

$ identify DSC_0001.JPG
DSC_0001.JPG JPEG 3872x2592

Voor de meeste toepassingen volstaat een Full HD-resolutie, ofwel 1920×1080:

$ mkdir small
$ convert -resize 1920x1080 DSC_0001.JPG small/DSC_0001.jpg

Convert maakt een verkleinde versie aan in de map small, weliswaar met een afmeting van 1613×1080 pixels. Dat komt doordat onze camera een andere beeldverhouding heeft: 3:2 in plaats van 16:9. In dat geval bewaart convert de kleinste afmeting van de lengte of hoogte (hier 1080) en past de andere afmeting volgens de beeldverhouding aan.

for-loop

Vervolgens schrijven we een for-loop rond het commando om alle bestanden met .JPG-extensie in de huidige map te verwerken:

$ for file in *.JPG; do convert -resize 1920x1080 $file small/$file; done

Wil je de verkleinde bestanden niet in een submap bewaren, maar met een aangepaste naam zoals <bestand>_small.jpg? Gebruik dan volgende syntax:

${file%.JPG}_small.jpg

Voor de nieuwe bestandsnaam vertrekken we van de oorspronkelijke naam, die in de variable ${file} staat. Met %.JPG verwijderen we die tekst aan het einde van de naam, waarna we _small.jpg eraan toevoegen.

xargs

Bevat jouw map bestanden met spaties in de bestandsnaam, dan werkt de for-loop helaas niet. Je gebruikt dan beter een find-commando in combinatie met xargs. Ons eerste voorbeeld ziet er dan als volgt uit:

$ find . -maxdepth 1 -name '*.JPG' -print0 | sed 's/.JPG//g' | xargs -0 -I% convert -resize 1920x1080 %.JPG small/%.jpg

Dat complexe commando is beter te begrijpen als je het een keer stap voor stap uitvoert. De -print0-optie bij find en de 0-optie bij xargs zijn cruciaal om geen problemen te veroorzaken met spaties in bestandsnamen. Met sed halen we de extensie van de oorspronkelijke naam weg. Dat laat ons toe om verderop in het xargs-commando de naam van het verkleinde bestand eenvoudiger te wijzigen. Het %-teken bij xargs bevat de aangepaste naam van het invoerbestand (zonder .JPG). Vergeet dus niet om extensies toe te voegen aan zowel het invoer- als het uitvoerbestand van convert!

Parallel

Het nadeel van de for-loop en find/xargs-combinatie is dat ze de bestanden één voor één verkleinen. Op moderne multicore systemen benut je dus slechts één cpu core. Om het proces te versnellen, start je beter evenveel processen op als er cpu cores zijn in je systeem. Een handige tool daarvoor is parallel (installeer het gelijknamige pakket). Ons voorbeeldcommando herschrijven we nu als volgt:

$ parallel convert -resize 1920x1080 '{}' 'small/{.}.jpg' ::: *.JPG

Parallel verwerkt de bestanden veel sneller én heeft als bijkomend voordeel een eenvoudige en beknopte syntax. Als laatste argument geef je de te verwerken bestanden op en in jouw commando gebruik je ‘{}’ als oorspronkelijke bestandsnaam en ‘{.}’ om de extensie van die naam te verwijderen. Makkelijk toch?

Share
December 2024
January 2025
No event found!

Related Topics