De beginnershandleiding voor Shell Scripting 2 voor loops
Als je je geek cred wilt opbouwen, kom dan bij ons voor de tweede aflevering in onze shell scripting-serie. We hebben enkele correcties, een paar verbeteringen aan het script van vorige week en een gids over lussen voor niet-ingewijden.
Het datecp-script Revisited
In het eerste deel van onze shell scripting guide hebben we een script gemaakt dat een bestand naar een back-upmap kopieerde nadat de datum aan het einde van de bestandsnaam was toegevoegd.
Samuel Dionne-Riel wees in de opmerkingen erop dat er een veel betere manier is om met onze variabele verwijzingen om te gaan.
Argumenten worden in de bash-shell in de ruimte gescheiden, het wordt tokenize als er een spatie in de resulterende uitgebreide opdracht is. In je script,
cp $ 1 $ 2. $ date_formatted
werkt zoals bedoeld zolang de uitgebreide variabelen geen spaties bevatten. Als je je script op deze manier belt:datecp "mijn oude naam" "mijn nieuwe naam"
de uitbreiding zal resulteren in deze opdracht:cp mijn nieuwe naam mijn oude naam. de datum
die eigenlijk 6 argumenten heeft.Om dit probleem op de juiste manier aan te pakken, zou de laatste regel van het script moeten zijn:
cp "$ 1" "$ 2. $ date_formatted"
Zoals je ziet, veranderde de regel van ons script van:
cp -iv $ 1 $ 2. $ date_formatted
naar:
cp -iv "$ 1" "$ 2". $ date_formatted
zorgt voor dit probleem wanneer het script wordt gebruikt voor bestanden met spaties in de naam. Samuel maakt ook het punt dat bij het kopiëren en plakken van code van deze site (of het internet in het algemeen) ervoor moet zorgen dat de juiste streepjes en aanhalingstekens worden vervangen door de "typografisch betere" lettertypen die ze vaak vervangen. We zullen ook meer doen om ervoor te zorgen dat onze code meer copy / paste-vriendelijk is. ;-)
Een andere commentator, Myles Braithwaite, besloot ons script uit te breiden zodat de datum vóór de bestandsextensie zou verschijnen. Dus in plaats van
tastyfile.mp3.07_14_11-12.34.56
we zouden dit krijgen:
tastyfile.07_14_11-12.34.56.mp3
wat uiteindelijk voor de meeste gebruikers een beetje handiger wordt. Zijn code is beschikbaar op zijn GitHub-pagina. Laten we eens kijken naar wat hij gebruikt om de bestandsnaam uit elkaar te halen.
date_formatted = $ (datum +% Y-% m-% d_% H.% M% S)
file_extension = $ (echo "$ 1" | awk -F. 'print $ NF')
file_name = $ (basename $ 1. $ file_extension)cp -iv $ 1 $ file_name- $ date_formatted. $ file_extension
Ik heb de opmaak een beetje gewijzigd, maar je kunt zien dat Myles zijn datumfunctie op regel 1 verklaart. In regel 2 gebruikt hij echter de opdracht "echo" waarbij het eerste argument van het script de bestandsnaam uitvoert. Hij gebruikt het pijpcommando om die uitvoer te nemen en het als invoer voor het volgende deel te gebruiken. Na de pijp roept Myles het "awk" -commando op, een krachtig patroon-scanprogramma. Met behulp van de vlag -F vertelt hij de opdracht dat het volgende teken (na een spatie) het "veldscheidingsteken" zal definiëren. In dit geval is dat een periode.
Zie nu een bestand met de naam "tastyfile.mp3" als samengesteld uit twee velden: "tastyfile" en "mp3". Ten slotte gebruikt hij
'print $ NF'
om het laatste veld weer te geven. In het geval dat uw bestand meerdere perioden heeft - en dus awk meerdere velden laat zien - zal het alleen de laatste weergeven, wat de bestandsextensie is.
In regel 3 maakt hij een nieuwe variabele voor de naam van het bestand en gebruikt de opdracht "basename" om te verwijzen naar alles in $ 1 behalve de bestandsextensie. Dit wordt gedaan door basisnaam te gebruiken en het $ 1 als argument te geven, en vervolgens een spatie en de bestandsextensie toe te voegen. De bestandsextensie wordt automatisch toegevoegd vanwege de variabele die verwijst naar regel 2. Wat dit zou doen is nemen
tastyfile.mp3
en verander het in
tastyfile
In de laatste regel stelde Myles het commando samen dat alles in de juiste volgorde uitvoert. Merk op dat er geen verwijzing is naar $ 2, een tweede argument voor het script. Dit specifieke script kopieert het bestand in plaats daarvan naar uw huidige map. Goed gedaan Samuel en Myles!
Scripts uitvoeren en $ PATH
We vermelden ook in ons Basics-artikel dat scripts standaard niet als opdrachten mogen worden gerefereerd. Dat wil zeggen, u moet naar het pad van het script wijzen om het uit te voeren:
./script
~ / Bin / script
Maar door uw scripts in ~ / bin / te plaatsen, kunt u gewoon hun naam overal typen om ze te laten uitvoeren.
Commenters hebben enige tijd gediscussieerd over hoe goed dit was, omdat geen moderne Linux distro standaard die map maakt. Bovendien voegt niemand het standaard toe aan de variabele $ PATH, wat is vereist om scripts als opdrachten te laten uitvoeren. Ik was een beetje verbaasd, want na het controleren van mijn variabele $ PATH hadden de commenters gelijk, maar het schrijven van scripts werkte nog steeds voor mij. Ik ontdekte waarom: veel moderne Linux-distributies creëren een speciaal bestand in de basismap van de gebruiker - .profile.
Dit bestand wordt gelezen door bash (tenzij .bash_profile aanwezig is in de basismap van de gebruiker) en onderaan is er een sectie die de ~ / bin / map aan de variabele $ PATH toevoegt als deze bestaat. Dus dat mysterie is opgelost. Voor de rest van de serie zal ik doorgaan met het plaatsen van scripts in de ~ / bin / map omdat het gebruikersscripts zijn en door gebruikers moeten kunnen worden uitgevoerd. En het lijkt erop dat we niet echt met de $ PATH-variabele handmatig hoeven te rommelen om dingen te laten werken.
Commando's herhalen met loops
Laten we naar een van de handigste tools in het nerdarsenaal gaan voor het omgaan met repetitieve taken: loops. Vandaag bespreken we 'voor' loops.
De basisbeschrijving van een for-loop is als volgt:
voor VARIABLE in LIST; do
command1
command2
...
commando-n
gedaan
VARIABLE kan elke variabele zijn, hoewel meestal de kleine letter "i" volgens afspraak wordt gebruikt. LIST is een lijst met items; u kunt meerdere items opgeven (ze scheiden door een spatie), verwijzen naar een extern tekstbestand of een asterisk (*) gebruiken om een bestand in de huidige map aan te geven. De weergegeven opdrachten zijn ingesprongen volgens conventie, dus het is gemakkelijker om te zien hoe je een nest maakt - loops in lussen zetten (zodat je kunt lus terwijl je een lus maakt).
Omdat lijsten spaties gebruiken als scheidingstekens - dat wil zeggen, een spatie betekent een verplaatsing naar het volgende item in de lijst - bestanden met spaties in de naam zijn niet erg vriendelijk. Laten we voorlopig blijven werken met bestanden zonder spaties. Laten we beginnen met een eenvoudig script om de namen van bestanden in de huidige map weer te geven. Maak een nieuw script in je ~ / bin / map getiteld "loopscript". Als je niet meer weet hoe je dit moet doen (inclusief het als uitvoerbaar bestand te markeren en de hash bang-hack toe te voegen), raadpleeg dan ons bash scripting basics-artikel.
Voer daarin de volgende code in:
voor i in item1 item2 item3 item4 item5 item6; do
echo "$ i"
gedaan
Wanneer u het script uitvoert, moet u die lijstitems gewoon als uitvoer krijgen.
Best simpel, toch? Laten we eens kijken wat er gebeurt als we de zaken een beetje veranderen. Verander je script zodat het dit zegt:
voor i in *; do
echo "$ i"
gedaan
Wanneer u dit script in een map uitvoert, krijgt u een lijst met bestanden die het bevat als uitvoer.
Laten we de echo-opdracht nu veranderen in iets nuttiger - bijvoorbeeld de opdracht zip. We zullen namelijk bestanden toevoegen aan een archief. En laten we wat argumenten in de mix krijgen!
voor i in $ @; do
zip-archief "$ i"
gedaan
Er is iets nieuws! "$ @" Is een snelkoppeling voor "$ 1 $ 2 $ 3 ... $ n". Met andere woorden, het is de volledige lijst van alle argumenten die u hebt opgegeven. Kijk nu wat er gebeurt als ik het script met verschillende invoerbestanden uitvoer.
U kunt zien welke bestanden zich in mijn map bevinden. Ik heb de opdracht met zes argumenten uitgevoerd en elk bestand is toegevoegd aan een gecomprimeerd archief met de naam "archive.zip". Makkelijk, toch?
Want loops zijn behoorlijk geweldig. Nu kunt u batch-functies uitvoeren op lijsten met bestanden. U kunt bijvoorbeeld alle argumenten van uw script kopiëren naar een gecomprimeerd archief, de originelen naar een andere map verplaatsen en het zip-bestand automatisch naar een externe computer kopiëren. Als je sleutelbestanden instelt met SSH, hoef je niet eens je wachtwoord in te voeren, en je kunt het script zelfs vertellen om het zipbestand te verwijderen nadat het is geüpload!
Het gebruik van for-loops maakt het gemakkelijk om een hele reeks acties te doen voor alle bestanden in een map. Je kunt een breed scala aan commando's samen stapelen en heel gemakkelijk argumenten gebruiken om een on-the-fly lijst te maken, en dit is slechts het topje van de ijsberg.
Bash scripters, heb je suggesties? Heb je een bruikbaar script gemaakt met loops? Wil je je gedachten over de serie delen? Laat enkele opmerkingen achter en help andere scriptnieuws!