Tips en Trucs 2022

Bestanden samenvoegen en sorteren

Er zijn heel wat manieren om bestanden in Linux samen te voegen en te sorteren. De keuze hangt grotendeels af van wat je wilt bereiken. Wil je bestanden gewoon samenvoegen tot één groot bestand of wil je de inhoud ondertussen ook zo ordenen dat het eenvoudiger wordt om deze te gebruiken.

Cat

Om een groep bestanden samen onder te brengen in één groot bestand is cat de eenvoudigste manier. De cat opdracht plakt de bestanden gewoon aan elkaar in de volgorde waarin je ze aanbiedt. Daarna stuur (redirect) je het resultaat naar een bestand.

Om dit te demonstreren maken we eerst drie bestanden aan met willekeurige woorden uit een spellingwoordenboek

dany@pindabook:~> shuf -n5 /usr/share/hunspell/nl_NL.dic | cut -d'/' -f1 > eerstebestand.txt
dany@pindabook:~> shuf -n5 /usr/share/hunspell/nl_NL.dic | cut -d'/' -f1 > tweedebestand.txt
dany@pindabook:~> shuf -n5 /usr/share/hunspell/nl_NL.dic | cut -d'/' -f1 > derdebestand.txt

De shuf opdracht haalt vijf (-n5) willekeurige woorden uit het /usr/share/hunspell/nl_NL.dic woordenboek dat gebruikt wordt door de LibreOffice spellingcontrole. Deze willekeurige woorden geven we door aan de cut opdracht die de voor ons overtollige tekens (spellingscontrole tekens) na het / teken verwijdert. Het resultaat daarvan wordt in een .txt bestand opgeslagen.

De vijf willekeurige woorden in het eerste tekstbestand kan je weergeven met:

dany@pindabook:~> cat eerstebestand.txt 
Robbendoes
dippen
Vandekerckhove
bongerd
doodschop

Als we de cat opdracht meerdere bestanden aanbieden, worden deze na elkaar weergegeven. En dit resultaat kunnen we opslaan (redirect) in het bestand samen.txt:

dany@pindabook:~> cat eerstebestand.txt tweedebestand.txt > samen.txt

Oeps, ik ben een bestand vergeten, je kunt het alsnog toevoegen met:

dany@pindabook:~> cat derdebestand.txt >> samen.txt

Let daarbij op het dubbele redirect-teken. M.a.w. één redirect-teken overschrijft een eventueel reeds bestaand bestand, twee redirect-tekens voegt iets achteraan toe aan een reeds bestaand bestand. Mocht het bestand toch niet bestaan wordt ook met een dubbel redirect-teken een nieuw bestand aangemaakt.

De proef op de som:

dany@pindabook:~> cat samen.txt 
Robbendoes
dippen
Vandekerckhove
bongerd
doodschop
Sanne
donderdag
dierenepen
oversimplificering
après-skiet
Verstraete
giraffe
lofzang
froufrous
pleeg

Als de bestandsnamen een bepaalde logica bevatten, kan je reguliere expressies gebruiken om de bestanden aan cat aan te bieden. In ons voorbeeld eindigen alle namen van onze voorbeeldbestanden op bestand.txt. We kunnen alle bestanden met een naam die eindigt op bestand.txt ook als volgt samenvoegen:

dany@pindabook:~> cat *bestand.txt > samen.txt

Om op voorhand te weten in welke volgorde de bestanden aan cat worden aangeboden, voer je een ls opdracht uit:

dany@pindabook:~> ls -1 *bestand.txt
derdebestand.txt
eerstebestand.txt
tweedebestand.txt

De -1 optie bij de ls opdracht zorgt dat de bestanden onder elkaar worden weergegeven (in één kolom).

Bestanden volgens leeftijd samenvoegen

Met een lus (loop) kan je alle bestanden doorlopen en doorgeven aan cat die ze dan aan elkaar plakt. Het voordeel van een lus is dat je de bestanden volgens een bepaald criteria kunt aanbieden. Met de ls opties t (time) en r (reverse) kunnen we de bestanden weergeven van oud naar recent:

dany@pindabook:~> ls -1 -tr *bestand.txt
eerstebestand.txt
tweedebestand.txt
derdebestand.txt

En inderdaad, ls presenteert de bestanden nu in de volgorde waarin ik ze heb aangemaakt. Om deze lijst bestanden in een lus te gebruiken, mag je de -1 optie om de uitvoer leesbaarder te maken weglaten.

Met de volgende opdracht bied je de bestanden volgens leeftijd via een lus aan de cat opdracht aan:

dany@pindabook:~> for file in `ls -tr *bestand.txt`; do  cat $file >> grootbestand.$$; done

De twee dollartekens ($$) stellen het process ID van de opdracht voor. Dit is een uniek identificatienummer dat aan deze opdracht werd toegekend, waardoor de kans zeer klein is dat je de bestanden toevoegt aan een bestaand bestand. De zo gevormde bestandsnaam achterhaal je met:

dany@pindabook:~> ls grootbestand.*
grootbestand.3628

Het process ID van mijn opdracht was dus 3628.

Bestanden gesorteerd samenvoegen

Je kunt de inhoud van de bestanden zowel voor als na het samenvoegen sorteren.

Alfabetisch sorteren

Om de inhoud na het samenvoegen te sorteren, geven we het resultaat van de cat op dracht door aan de sort opdracht:

dany@pindabook:~> cat eerstebestand.txt tweedebestand.txt derdebestand.txt | sort > gesorteerdbestand.txt
dany@pindabook:~> cat gesorteerdbestand.txt 
après-skiet
bongerd
dierenepen
dippen
donderdag
doodschop
froufrous
giraffe
lofzang
oversimplificering
pleeg
Robbendoes
Sanne
Vandekerckhove
Verstraete

Om de bestandsinhoud eerst te sorteren en daarna pas samen te voegen, gebruik je een lus:

dany@pindabook:~> rm gesorteerdbestand.txt
dany@pindabook:~> for file in `ls *bestand.txt`; do sort $file >> gesorteerdbestand.txt; done
dany@pindabook:~> cat gesorteerdbestand.txt 
froufrous
giraffe
lofzang
pleeg
Verstraete
bongerd
dippen
doodschop
Robbendoes
Vandekerckhove
après-skiet
dierenepen
donderdag
oversimplificering
Sanne

Let op het verwijderen van het reeds bestaande gesorteerdbestand.txt bestand. Dit is noodzakelijk, want binnen de lus gebruiken we twee redirect-tekens waardoor we telkens gegevens aan het bestand toevoegen.

Numeriek sorteren

Eerst zorgen we weer voor wat testbestanden met willekeurige getallen tussen 0 en 999:

dany@pindabook:~> shuf -i 0-999 -n 5 > eerstebestand.txt 
dany@pindabook:~> shuf -i 0-999 -n 5 > tweedebestand.txt 
dany@pindabook:~> shuf -i 0-999 -n 5 > derdebestand.txt 
dany@pindabook:~> cat eerstebestand.txt 
304
423
186
12
990

Met de -n optie van sort zorg je ervoor dat er numeriek wordt gesorteerd. Let wel de getallen moeten aan het begin van de regel staan:

dany@pindabook:~> cat eerstebestand.txt tweedebestand.txt derdebestand.txt | sort -n > gesorteerdbestand.txt
dany@pindabook:~> cat gesorteerdbestand.txt 
12
132
186
250
257
281
281
304
423
601
745
755
756
826
990

Als je datums wilt sorteren, zorg je er best voor dat deze in een voor sort verwerkbaar formaat staan. Zoals 2022-01-18 of 2022/01/18 (jaar, maand en dag formaat). Andere datumformaten sorteren wordt een stuk complexer. We maken terug enkele testbestanden aan met willekeurige datums tussen 1 januari 2000 en 18 januari 2022:

dany@pindabook:~> shuf -n5 -i$(date -d '2000-01-01' '+%s')-$(date -d '2021-01-18' '+%s') | xargs -I{} date -d '@{}' '+%Y/%m/%d' > eerstebestand.txt 
dany@pindabook:~> shuf -n5 -i$(date -d '2000-01-01' '+%s')-$(date -d '2021-01-18' '+%s') | xargs -I{} date -d '@{}' '+%Y/%m/%d' > tweedebestand.txt 
dany@pindabook:~> shuf -n5 -i$(date -d '2000-01-01' '+%s')-$(date -d '2021-01-18' '+%s') | xargs -I{} date -d '@{}' '+%Y/%m/%d' > derdebestand.txt 
dany@pindabook:~> cat eerstebestand.txt 
2015/06/21
2007/08/25
2015/08/06
2009/01/13
2000/12/15

We kunnen dit op dezelfde manier numeriek samenvoegen en sorteren:

dany@pindabook:~> cat eerstebestand.txt tweedebestand.txt derdebestand.txt | sort -n > gesorteerdbestand.txt
dany@pindabook:~> cat gesorteerdbestand.txt 
2000/12/15
2002/11/24
2003/06/05
2006/08/18
2006/08/29
2007/03/21
2007/08/25
2009/01/13
2009/02/20
2012/09/06
2015/06/21
2015/08/06
2017/11/15
2018/09/11
2020/10/16

Paste

De paste opdracht zorgt dat bestanden per regel worden samengevoegd. M.a.w. de eerste regel van het samengevoegde bestand bevat de eerste regels van alle bestanden. Om dit te demonstreren, maken we opnieuw enkele testbestanden aan. Deze keer met gestructureerde gegevens, zodat we goed de werking van paste kunnen demonstreren:

dany@pindabook:~> echo -e "A één\nA twee\nA drie" > eerstebestand.txt 
dany@pindabook:~> echo -e "B één\nB twee\nB drie\nB vier" > tweedebestand.txt 
dany@pindabook:~> echo -e "C één\nC twee\nC drie\nC vier\nC vijf" > derdebestand.txt 
dany@pindabook:~> cat eerstebestand.txt 
A één
A twee
A drie

Als we de paste opdracht op de drie testbestanden loslaten, krijgen we:

dany@pindabook:~> paste eerstebestand.txt tweedebestand.txt derdebestand.txt 
A één   B één   C één
A twee  B twee  C twee
A drie  B drie  C drie
        B vier  C vier
                C vijf

Redirect het naar een bestand om het resultaat op te slaan:

dany@pindabook:~> paste eerstebestand.txt tweedebestand.txt derdebestand.txt > samen.txt 
dany@pindabook:~> cat samen.txt 
A één   B één   C één
A twee  B twee  C twee
A drie  B drie  C drie
        B vier  C vier
                C vijf

Anderzijds kan je met paste (-s optie) elk bestand op één regel plaatsen:

dany@pindabook:~> paste -s eerstebestand.txt tweedebestand.txt derdebestand.txt 
A één   A twee  A drie
B één   B twee  B drie  B vier
C één   C twee  C drie  C vier  C vijf

Join

De join opdracht gebruik je als je bestanden op basis van een bepaald veld wilt samenvoegen. Zo kan je een bestand met namen en bijhorende telefoonnummers samenvoegen met een bestand met dezelfde namen maar met de e-mail adressen. Een belangrijke beperking is echter dat de regels met gegevens in elk bestand in dezelfde volgorde moeten staan. We maken een testbestand met namen en bijhorende telefoonnummers:

dany@pindabook:~> echo "Sandra 0123-11-11-11" > telefoonnummers.txt
dany@pindabook:~> echo "Luc 0123-22-22-22" >> telefoonnummers.txt
dany@pindabook:~> echo "Peter 0123-33-33-33" >> telefoonnummers.txt
dany@pindabook:~> echo "Roos 0123-44-44-44" >> telefoonnummers.txt
dany@pindabook:~> echo "Elke 0123-55-55-55" >> telefoonnummers.txt
dany@pindabook:~> cat telefoonnummers.txt 
Sandra 0123-11-11-11
Luc 0123-22-22-22
Peter 0123-33-33-33
Roos 0123-44-44-44
Elke 0123-55-55-55

En een bestand met dezelfde namen in dezelfde volgorde, maar nu met de e-mail adressen:

dany@pindabook:~> echo "Sandra sandra@gmail.com" > email.txt
dany@pindabook:~> echo "Luc" >> email.txt
dany@pindabook:~> echo "Peter peter@gmail.com" >> email.txt
dany@pindabook:~> echo "Roos roos@gmail.com" >> email.txt
dany@pindabook:~> echo "Elke elke@gmail.com" >> email.txt
dany@pindabook:~> cat email.txt 
Sandra sandra@gmail.com
Luc
Peter peter@gmail.com
Roos roos@gmail.com
Elke elke@gmail.com

De join opdracht kan deze gegevens samenvoegen:

dany@pindabook:~> join telefoonnummers.txt email.txt 
Sandra 0123-11-11-11 sandra@gmail.com
Luc 0123-22-22-22
Peter 0123-33-33-33 peter@gmail.com
Roos 0123-44-44-44 roos@gmail.com
Elke 0123-55-55-55 elke@gmail.com

In dit voorbeeld moet het eerste veld met de naam in elk bestand voorkomen, ook al ontbreekt de extra informatie.

Samenvoegen en sorteren

Gebruik de helppagina's

Om alle mogelijkheden en de correcte werking van deze opdrachten te achterhalen, raadpleeg je best de handleiding van de verschillende opdrachten. Dit kan bijvoorbeeld met de --help optie bij de verschillende opdrachten en/of met de man opdracht gevolgd door de naam van de opdracht:

dany@pindabook:~> join --help
Gebruik:  join [OPTIE...] BESTAND1 BESTAND2

Stuurt voor elk paar invoerregels met identieke samenvoegvelden een regel
naar standaarduitvoer.  Het standaard samenvoegveld is het eerste veld,
afgebakend door spaties of tabs.

Wanneer BESTAND1 of BESTAND2 (niet beide) '-' is, wordt standaardinvoer gelezen.

  -a NUMMER          ook onpaarbare regels uit bestand NUMMER tonen, waar NUMMER
                       1 of 2 is, overeenkomend met BESTAND1 of BESTAND2
  -e TEKST           ontbrekende invoervelden vervangen door "TEKST"
  -i, --ignore-case  verschil in hoofd- en kleine letters negeren
  -j VELD            hetzelfde als '-1 VELD -2 VELD'
  -o OPMAAK          te gebruiken opmaak (zie onder) van elke uitvoerregel
  -t TEKEN           te gebruiken veldscheidingsteken voor invoer en uitvoer
  -v NUMMER          als '-a NUMMER', maar samengevoegde regels onderdrukken
  -1 VELD            samenvoegen op dit VELD in bestand 1
  -2 VELD            samenvoegen op dit VELD in bestand 2
  --check-order      controleren of de invoer juist gesorteerd is, zelfs als
                       van alle invoerregels paren gemaakt kunnen worden
  --nocheck-order    niet controleren of de invoer juist gesorteerd is
  --header           de eerste regel in elk bestand als veldkoppen behandelen;
                       deze samenvoegen zonder proberen ze te paren
  -z, --zero-terminated      regels afsluiten met 0-byte, niet met nieuweregel
      --help      deze hulptekst tonen en stoppen
      --version   programmaversie tonen en stoppen

Standaard is voorloopwitruimte de veldscheiding en wordt verder genegeerd.
Als '-t TEKEN' gegeven is, worden velden gescheiden door TEKEN.  Elk VELD
heeft een veldnummer, tellend vanaf 1.

OPMAAK is een komma- of spatiegescheiden lijst aanduidingen, elk van de vorm
'NUMMER.VELD' of '0', waar NUMMER het bestandsnummer en VELD het veldnummer is.
De standaardopmaak voert eerst het samenvoegveld uit, dan de overige velden van
BESTAND1, en dan de overige velden van BESTAND2, alles gescheiden door TEKEN.
Als OPMAAK het sleutelwoord 'auto' is, dan bepaalt de eerste regel van elk
bestand hoeveel velden er voor elke regel uitgevoerd worden.

Belangrijk: BESTAND1 en BESTAND2 moeten gesorteerd zijn op de samenvoegvelden.
Gebruik bijvoorbeeld "sort -k 1b,1" als aan 'join' geen opties gegeven worden
of gebruik "join -t" als 'sort' geen opties heeft.

Opmerking: het vergelijken volgt de regels gespecificeerd door 'LC_COLLATE'.
Als de invoer niet gesorteerd is en sommige regels niet samengevoegd kunnen
worden, dan wordt er een waarschuwing gegeven.

Online hulp bij GNU coreutils: 
Meld vertalingsfouten aan .
Full documentation 
of lokaal via: info '(coreutils) join invocation'
dany@pindabook:~> man join
Man: alle passende pagina's vinden, niet alleen eerste (set MAN_POSIXLY_CORRECT to avoid this)
 * join (1)
   join (1+2)
   join (n)
   join (1p)
Man: Welke man-pagina wilt u zien?
Man:

Een man pagina verlaat je met een druk op de q-toets.

Alles opruimen

De testbestanden en samengevoegde bestanden verwijder je met:

dany@pindabook:~> rm eerstebestand.txt tweedebestand.txt derdebestand.txt samen.txt email.txt gesorteerdbestand.txt grootbestand.3628 telefoonnummers.txt

Bij jullie zal het bestand grootbestand.3628 eindigen met een ander getal (proces ID).