
Met directories op Linux kun je bestanden groeperen in verschillende, aparte collecties. Het nadeel is dat het vervelend wordt om van map naar map te gaan om een repetitieve taak uit te voeren. Hier leest u hoe u dat kunt automatiseren.
Alles over mappen
Het eerste commando dat je leert wanneer je kennismaakt met Linux is waarschijnlijk ls
maar cd
zal er niet ver achter zitten. Het begrijpen van mappen en hoe je er omheen kunt bewegen, met name geneste submappen, is een fundamenteel onderdeel van het begrijpen hoe Linux zichzelf organiseert en hoe je je eigen werk kunt organiseren in bestanden, mappen en submappen.
Het concept van een boom met mappen begrijpen – en hoe je ertussen kunt bewegen – is een van de vele kleine mijlpalen die je passeert als je vertrouwd raakt met het landschap van Linux. Gebruik makend van cd
met een pad brengt u naar die map. Snelkoppelingen zoals cd ~
of cd
op zichzelf brengt u terug naar uw homedirectory, en cd ..
brengt u een niveau omhoog in de directorystructuur. Gemakkelijk.
Er is echter geen even eenvoudige manier om een opdracht in alle mappen van een mappenboom uit te voeren. Er zijn verschillende manieren waarop we die functionaliteit kunnen bereiken, maar er is geen standaard Linux-commando voor dat doel.
Sommige commando’s, zoals ls
opdrachtregelopties hebben die hen dwingen te werken recursief, wat betekent dat ze in één map beginnen en methodisch de hele mappenboom onder die map doorlopen. Voor ls
het is de -R
(recursieve) optie.
Als u een opdracht moet gebruiken die recursie niet ondersteunt, moet u zelf de recursieve functionaliteit leveren. Hier is hoe dat te doen.
De boom Commando
De tree
commando zal ons niet helpen met de taak die voor ons ligt, maar het maakt het wel gemakkelijk om de structuur van een mappenboom te zien. Het tekent de boomstructuur in een terminalvenster zodat we direct een overzicht kunnen krijgen van de mappen en submappen die de mappenboom vormen, en hun relatieve posities in de boomstructuur.
U moet installeren tree
.
Op Ubuntu moet je typen:
sudo apt install tree
Gebruik op Fedora:
sudo dnf install tree
Op Manjaro is het commando:
sudo pacman -Sy tree
Gebruik makend van tree
zonder parameters tekent de boom onder de huidige map.
tree
Je kunt een pad passeren naar tree
op de opdrachtregel.
tree work
De -d
(mappen) optie sluit bestanden uit en toont alleen mappen.
tree -d work
Dit is de handigste manier om een duidelijk beeld te krijgen van de structuur van een directorystructuur. De hier getoonde directorystructuur is degene die in de volgende voorbeelden wordt gebruikt. Er zijn vijf tekstbestanden en acht mappen.
Ontleed de uitvoer van ls niet naar Traverse Directories
Je eerste gedachte zou kunnen zijn, als ls
kan recursief een mappenboom doorlopen, waarom niet gebruiken ls
om precies dat te doen en de uitvoer in een aantal andere opdrachten te pipen die de mappen ontleden en enkele acties uitvoeren?
De uitvoer van . parseren ls
wordt beschouwd als een slechte praktijk. Vanwege de mogelijkheid in Linux om bestands- en mapnamen te maken die allerlei vreemde tekens bevatten, wordt het erg moeilijk om een generieke, universeel correcte parser te maken.
Je zou nooit willens en wetens een mapnaam maken die zo belachelijk is als deze, maar een fout in een script of een toepassing kan dat wel zijn.
Het parseren van legitieme maar slecht overwogen bestands- en directorynamen is foutgevoelig. Er zijn andere methoden die we kunnen gebruiken die veiliger en veel robuuster zijn dan te vertrouwen op het interpreteren van de output van ls
.
Het zoekcommando gebruiken
De find
commando heeft ingebouwde recursieve mogelijkheden, en het heeft ook de mogelijkheid om commando’s voor ons uit te voeren. Zo bouwen we krachtige oneliners. Als het iets is dat u in de toekomst waarschijnlijk wilt gebruiken, kunt u van uw oneliner een alias of shell-functie maken.
Deze opdracht doorloopt recursief de directorystructuur, op zoek naar directory’s. Elke keer dat het een directory vindt, drukt het de naam van de directory af en herhaalt het zoeken in die directory. Nadat het zoeken in één map is voltooid, verlaat het die map en hervat het zoeken in de bovenliggende map.
find work -type d -execdir echo "In:" {} ;
U kunt zien aan de volgorde waarin de mappen worden weergegeven, hoe de zoekopdracht door de boom gaat. Door de uitvoer van de te vergelijken tree
commando naar de uitvoer van de find
oneliner, je zult zien hoe find
doorzoekt elke directory en subdirectory om de beurt totdat het een directory zonder subdirectories bereikt. Het gaat dan een niveau terug en hervat het zoeken op dat niveau.
Hier is hoe de opdracht is samengesteld.
-
vind: De
find
opdracht. - werk: De directory om de zoekopdracht in te starten. Dit kan een pad zijn.
- -type d: We zijn op zoek naar mappen.
- -execdir: We gaan een commando uitvoeren in elke directory die we vinden.
- echo “In:” {}: Dit is de opdracht., We herhalen gewoon de naam van de map naar het terminalvenster. De “{}” bevat de naam van de huidige map.
- ;: Dit is een puntkomma die wordt gebruikt om de opdracht te beëindigen. We moeten er met de backslash aan ontsnappen, zodat Bash het niet direct interpreteert.
Met een kleine verandering kunnen we ervoor zorgen dat de opdracht find bestanden retourneert die overeenkomen met een zoekaanwijzing. We moeten de -name optie en een zoek aanwijzing opnemen. In dit voorbeeld zoeken we naar tekstbestanden die overeenkomen met “*.txt”, en hun naam herhalen in het terminalvenster.
find work -name "*.txt" -type f -execdir echo "Found:" {} ;
Of u nu naar bestanden of mappen zoekt, hangt af van wat u wilt bereiken. Een opdracht uitvoeren: in elke mapgebruiken -type d
. Een opdracht uitvoeren op elk overeenkomend bestandgebruiken -type f
.
Deze opdracht telt de regels in alle tekstbestanden in de startdirectory en subdirectories.
find work -name "*.txt" -type f -execdir wc -l {} ;
Directory-bomen doorkruisen met een script
Als u door mappen in een script moet gaan, kunt u de find
commando in je script. Als u de recursieve zoekopdrachten zelf moet of wilt doen, kunt u dat ook doen.
#!/bin/bash shopt -s dotglob nullglob function recursive { local current_dir dir_or_file for current_dir in $1; do echo "Directory command for:" $current_dir for dir_or_file in "$current_dir"/*; do if [[ -d $dir_or_file ]]; then recursive "$dir_or_file" else wc $dir_or_file fi done done } recursive "$1"
Kopieer de tekst naar een editor en sla deze op als “recurse.sh”, gebruik vervolgens de chmod
commando om het uitvoerbaar te maken.
chmod +x recurse.sh
Het script stelt twee shell-opties in, dotglob
en nullglob
.
De dotglob
instelling betekent bestands- en directorynamen die beginnen met een punt “.
” wordt geretourneerd wanneer zoektermen met jokertekens worden uitgebreid. Dit betekent in feite dat we verborgen bestanden en mappen opnemen in onze zoekresultaten.
De nullglob
instelling betekent dat zoekpatronen die geen resultaten opleveren, worden behandeld als een lege of null-tekenreeks. Ze gebruiken niet standaard de zoekterm zelf. Met andere woorden, als we naar alles in een map zoeken met behulp van het asterisk-jokerteken “*
“, maar er zijn geen resultaten, we ontvangen een null-tekenreeks in plaats van een tekenreeks met een asterisk. Dit voorkomt dat het script per ongeluk een map met de naam “*” probeert te openen of “*” als een bestandsnaam behandelt.
Vervolgens definieert het een functie genaamd recursive
. Dit is waar de interessante dingen gebeuren.
Er worden twee variabelen gedeclareerd, genaamd current_dir
en dir_or_file
. Dit zijn lokale variabelen en er kan alleen binnen de functie naar worden verwezen.
Een variabele genaamd $1
wordt ook gebruikt binnen de functie. Dit is de eerste (en enige) parameter die aan de functie wordt doorgegeven wanneer deze wordt aangeroepen.
Het script gebruikt twee for
lussen, de ene genest in de andere. De eerste (buitenste) for
lus wordt voor twee dingen gebruikt.
Een daarvan is om de opdracht uit te voeren die u in elke map wilt laten uitvoeren. Het enige dat we hier doen, is de naam van de map naar het terminalvenster herhalen. Je kunt natuurlijk elk commando of een reeks commando’s gebruiken, of een andere scriptfunctie aanroepen.
Het tweede dat de buitenste for-lus doet, is alle bestandssysteemobjecten die het kan vinden controleren, dit zijn bestanden of mappen. Dit is het doel van de innerlijke for
lus. Op zijn beurt wordt elk bestand of elke mapnaam doorgegeven aan de dir_or_file
variabel.
De dir_or_file
variabele wordt vervolgens getest in een if-statement om te zien of het een directory is.
- Als dat zo is, roept de functie zichzelf aan en geeft de naam van de directory door als parameter.
- Als de
dir_or_file
variabele geen directory is, dan moet het een bestand zijn. Alle opdrachten die u op het bestand wilt toepassen, kunnen worden aangeroepen vanaf deelse
clausule van deif
uitspraak. Je zou ook een andere functie binnen hetzelfde script kunnen aanroepen.
De laatste regel in het script noemt de recursive
functie en passeert in de eerste opdrachtregel parameter $1
als de startdirectory om in te zoeken. Dit is het begin van het hele proces.
Laten we het script uitvoeren.
./recurse.sh work
De directory’s worden doorlopen en het punt in het script waar een opdracht in elke directory zou worden uitgevoerd, wordt aangegeven door de regels “Directory-opdracht voor:”. Gevonden bestanden hebben de wc
commando uitvoeren om regels, woorden en karakters te tellen.
De eerste directory die wordt verwerkt is “work”, gevolgd door elke geneste directory-tak van de boom.
Een interessant punt om op te merken is dat je de volgorde waarin de mappen worden verwerkt, kunt wijzigen door de mapspecifieke opdrachten te verplaatsen van boven de inner for-lus naar eronder.
Laten we de regel “Directory-opdracht voor:” verplaatsen naar na de done
van de innerlijke for
lus.
#!/bin/bash shopt -s dotglob nullglob function recursive { local current_dir dir_or_file for current_dir in $1; do for dir_or_file in "$current_dir"/*; do if [[ -d $dir_or_file ]]; then recursive "$dir_or_file" else wc $dir_or_file fi done echo "Directory command for:" $current_dir done } recursive "$1"
Nu zullen we het script nog een keer uitvoeren.
./recurse.sh work
Deze keer hebben de mappen de commando’s toegepast vanaf de diepste niveaus eerst, werkend terug naar de takken van de boom. De directory die als parameter aan het script is doorgegeven, wordt als laatste verwerkt.
Als het belangrijk is om eerst diepere mappen te laten verwerken, dan is dit hoe je dat kunt doen.
Recursie is raar
Het is alsof je jezelf belt op je eigen telefoon en een bericht voor jezelf achterlaat om jezelf te vertellen wanneer je je de volgende keer ontmoet – herhaaldelijk.
Het kan wat moeite kosten voordat je de voordelen ervan doorhebt, maar als je dat doet, zul je zien dat het een programmatisch elegante manier is om moeilijke problemen aan te pakken.