Tips en Trucs 2024

Een groot aantal bestanden kopiëren of verplaatsen

Er zijn een paar methodes om snel en eenvoudig een groot aantal bestanden te verplaatsen of te kopiëren in Linux. Eén zo'n methode is het gebruik van de opdrachten cp, tar en rsync, waarmee je snel en gemakkelijk grote aantallen bestanden kunt kopiëren en verplaatsen, maar ze hebben een bepaalde drempel waarna de opdracht een foutmelding geeft.

Een groot aantal lege bestanden aanmaken

Voor de demonstratie maken we 500.000 lege bestanden aan met de touch opdracht, wat naargelang de snelheid van jouw systeem een hele tijd kan duren. Daarna kunnen we onze opdrachten testen op de nieuw aangemaakte bestanden.

dany@pindabook:~$ for i in {1..500000};do touch bestand$i.txt; done

Daar dit een hele tijd kan duren, kan het interessant zijn om de voortgang ervan te volgen in een tweede terminal. Met de volgende opdracht geef je de tien meest recent aangemaakte bestanden weer:

dany@pindabook:~$ ls -tr | tail
bestand179623.txt
bestand179626.txt
bestand179631.txt
bestand179634.txt
bestand179636.txt
bestand179640.txt
bestand179642.txt
bestand179645.txt
bestand179652.txt
bestand179662.txt

Massa's bestanden verplaatsen

Eerst maken we een map aan waar we de bestanden naar toe willen verplaatsen:

dany@pindabook:~$ mkdir test-arena

Laten we de mv opdracht uitvoeren om alle aangemaakte testbestanden naar de nieuwe map te verplaatsen. Zoals verwacht geeft de terminal -bash: /usr/bin/mv: Argumentenlijst is te lang weer.

dany@pindabook:~$ mv bestand* test-arena/
-bash: /usr/bin/mv: Argumentenlijst is te lang

De maximale lengte van de argumentenlijst is overschreden en kan je achterhalen met de opdracht:

dany@pindabook:~$ getconf ARG_MAX
2097152

Zelfs het opvragen van een lijst met bestanden is niet meer mogelijk.

dany@pindabook:~$ ls bestand*
-bash: /usr/bin/ls: Argumentenlijst is te lang

Het opzoeken van de te gebruiken bestanden met de zoek parameter bestand* produceert dus een lijst met bestandsnamen die langer is dan 2097152 tekens.

Het probleem oplossen met find

De find opdracht werkt niet met de argumentenlijst, maar geeft de naam van elk gevonden bestand direct weer.

dany@pindabook:~$ find . -maxdepth 1 -name "bestand*"
...
./bestand261618.txt
./bestand484457.txt
./bestand481493.txt

Dit werkt trager, maar zonder limiet. De find opdracht zoekt daarbij in de huidige map (.) naar bestanden waarvan de naam voldoet aan de zoekparameter -name "bestand*" zonder daarbij af te dalen in eventuele submappen (-maxdepth 1). Door nu de gevonden bestanden in een tekstbestand op te slaan en daarna bewerkingen op elke bestandsnaam in het tekstbestand uit te voeren, kunnen we toch de argumentenlijst limiet omzeilen. Eerst maken we een tijdelijk tekstbestand aan om de bestandsnamen op te slaan.

dany@pindabook:~$ TMP_FILE=$(mktemp -q /tmp/bestand.XXXXXX)
dany@pindabook:~$ echo $TMP_FILE
/tmp/bestand.F37DGk

De tweede opdracht toont ons (op mijn systeem) de map en bestandnaam van het tijdelijk tekstbestand. Daar dit zich in de /tmp/ map bevindt, wordt dit niet op een schijf maar in het werkgeheugen van het systeem bewaard, wat de snelheid ten goede komt. De door de find opdracht gevonden bestanden, sla je in het tijdelijk tekstbestand op met:

dany@pindabook:~$ find . -maxdepth 1 -name "bestand*" > $TMP_FILE

Na het voltooien van de find opdracht kan je het resultaat snel testen met:

dany@pindabook:~$ tail $TMP_FILE 
./bestand473418.txt
./bestand194855.txt
./bestand178222.txt
./bestand51422.txt
./bestand230850.txt
./bestand172434.txt
./bestand29692.txt
./bestand261618.txt
./bestand484457.txt
./bestand481493.txt

Zoals je merkt, staan de bestanden in een willekeurige volgorde.
Copy
Het aantal gevonden bestanden komt echter overeen met het aantal regels in het tekstdocument dat je als volgt kunt achterhalen:

dany@pindabook:~$ wc -l $TMP_FILE 
500000 /tmp/bestand.F37DGk

Nu rest ons alleen nog in een lus (while ...; ... ; done) elke regel van het tekstbestand (< $TMP_FILE) in te lezen (read -r line) en er een verplaats (mv) opdracht op uit te voeren.

dany@pindabook:~$ while read -r line; do mv -v "$line" test-arena/; done < $TMP_FILE
...
hernoemd './bestand261618.txt' -> 'test-arena/bestand261618.txt'
hernoemd './bestand484457.txt' -> 'test-arena/bestand484457.txt'
hernoemd './bestand481493.txt' -> 'test-arena/bestand481493.txt'

Na een tijd (wees geduldig) kunnen we controleren of de verplaats opdracht is geslaagd.

dany@pindabook:~$ find test-arena/ -maxdepth 1 -name "bestand*" > $TMP_FILE
dany@pindabook:~$ wc -l $TMP_FILE 
500000 /tmp/bestand.F37DGk
dany@pindabook:~$ tail $TMP_FILE 
test-arena/bestand499943.txt
test-arena/bestand499947.txt
test-arena/bestand499951.txt
test-arena/bestand499967.txt
test-arena/bestand499972.txt
test-arena/bestand499974.txt
test-arena/bestand499977.txt
test-arena/bestand499981.txt
test-arena/bestand499998.txt
test-arena/bestand500000.txt

Of dit ook werkt voor een kopieer opdracht testen we met de volgende opdrachten:

dany@pindabook:~$ mkdir test-dir
dany@pindabook:~$ while read -r line; do cp -v "$line" test-dir/; done < $TMP_FILE
...
'test-arena/bestand499981.txt' -> 'test-dir/bestand499981.txt'
'test-arena/bestand499998.txt' -> 'test-dir/bestand499998.txt'
'test-arena/bestand500000.txt' -> 'test-dir/bestand500000.txt'

Opdrachten rechstreeks met find uitvoeren

Eigenlijk hebben we het tijdelijk bestand niet nodig en kunnen we binnen de find opdracht de zoekresultaten doorgeven aan een andere opdracht. Bijvoorbeeld de echo opdracht om de gevonden bestandsnaam weer te geven. Daarvoor gebruiken we de find optie -exec echo {} \; (let op de laatste twee tekens). {} stelt daarbij de gevonden bestanden voor.

dany@pindabook:~$ find test-dir/ -maxdepth 1 -name "bestand*" -exec echo {} \;
...
test-dir/bestand499981.txt
test-dir/bestand499998.txt
test-dir/bestand500000.txt

We kunnen dit testen door de bestanden in de map test-dir te verwijderen:

dany@pindabook:~$ find test-dir/ -maxdepth 1 -name "bestand*" -exec rm {} \;

En inderdaad de bestanden zijn verdwenen:

dany@pindabook:~$ ls test-dir/

De map zelf verwijder je met:

dany@pindabook:~$ rm -r test-dir

Het verwijderen van een map, zelf één met massa's bestanden kan ook door de map recursief te verwijderen:

dany@pindabook:~$ rm -r test-arena/

Of dit gelukt is, kan je nagaan met:

dany@pindabook:~$ ls bestand*
ls: kan geen toegang krijgen tot 'bestand*': Bestand of map bestaat niet

Blijkbaar bestaat de map niet meer, missie geslaagd. Het tijdelijke tekstbestand verwijder je met:

dany@pindabook:~$ rm $TMP_FILE