For en Read-While-lussen in Bash

Hoe te herhalen, oftewel het ontwerpen van een programma om repetitief werk voor je te doen

De lus is een van de meest fundamentele en krachtige constructies in computers, omdat het ons in staat stelt een reeks commando’s zo vaak als we willen te herhalen op een lijst met items naar keuze. Veel computationeel denken omvat het nemen van één taak en het oplossen ervan op een manier die herhaaldelijk kan worden toegepast op alle andere soortgelijke taken, en de for-lus is hoe we de computer dat repetitieve werk laten doen:

In tegenstelling tot de meeste code die we tot nu toe” hebben geschreven bij de interactieve prompt, wordt een for-lus “niet uitgevoerd zodra we op Enter drukken:

We kunnen zoveel commando’s schrijven als we willen in het blok tussen de do en done sleutelwoorden :

Alleen totdat we done bereiken en op Enter drukken, doet de for-loop zijn werk.

Dit is fundamenteel anders dan het regel-voor-regel commando-en-antwoord dat we tot dusver hebben ervaren bij de prompt. En het geeft aan hoe we verder gaan programmeren: minder nadruk op het uitvoeren van commando’s met elke regel, en meer nadruk op het plannen van de functionaliteit van een programma, en het later uitvoeren.

Basissyntaxis

De syntaxis voor for -lussen kan verwarrend zijn, dus hier zijn enkele basisvoorbeelden om uw begrip ervan voor te bereiden / op te frissen:

Hier “is een meer uitgebreide versie met variabelen:

Een opdrachtvervanging kan worden gebruikt om de items die de for -lus herhaalt:

Als u een lijst met regels van een bestand, en zijn er absoluut zeker van dat geen van de regels een spatie bevat:

Een read-while-lus is een variatie op het bovenstaande, maar is veiliger voor het lezen van regels uit een bestand:

Een basis for-lus construeren

Laten we beginnen bij het begin, met een zeer minimal for loop, en bouwde het vervolgens in iets uitgebreider, om ons te helpen hun doel te begrijpen.

De eenvoudigste loop

Dit is ongeveer net zo eenvoudig aangezien je een for-lus kunt maken:

Lijkt dat behoorlijk waardeloos? Ja, het zou moeten zijn. Ik heb vier regels code geschreven om te doen waarvoor één regel nodig is, echo "Hi".

Meer elementen in de verzameling

‘is moeilijk te zeggen, maar een’ lus ‘is uitgevoerd. Het is slechts één keer uitgevoerd. Oké, dus hoe zorgen we ervoor dat het meer dan één keer wordt uitgevoerd? Voeg meer (door spaties gescheiden) elementen toe rechts van de in trefwoord. Laten we er nog vier 1 “s toevoegen:

OK, niet erg spannend, maar het programma leek zeker tenminste een lus te maken: vier 1 “s resulteerden in het uitvoeren van vier echo commando’s.

Wat gebeurt er als we die vier 1 “s vervangen door verschillende getallen? En misschien een paar woorden?

En… niets. Dus de lus doet niet automatisch iets specifieks voor de verzameling waarden die we eraan hebben gegeven. Nog niet.

Verwijs naar de lusvariabele

Laten we naar de linkerkant van het in sleutelwoord kijken, en naar dat x. Wat “is het punt van die x? Een kleine letter x is niet de naam van een trefwoord of commando dat we tot nu toe zijn tegengekomen (en als je het alleen uitvoert bij de prompt, krijg je een foutmelding). Dus misschien “is het een variabele? Laten we proberen ernaar te verwijzen in de echo instructie:

Bingo. Dit is vrijwel de fundamentele werking van een for -lus: – Haal een verzameling items / waarden op (Q Zebra 999 Smithsonian) – Geef ze door aan een for lusconstruct – Gebruik de lusvariabele (x) als een tijdelijke aanduiding en schrijf opdrachten tussen de do / done block. – Wanneer de lus wordt uitgevoerd, neemt de lusvariabele, x, de waarde van elk van de items in de lijst – Q, Zebra, 999, Smithsonian, – en het blok met opdrachten tussen do en done wordt dan uitgevoerd. Deze reeks wordt eenmaal herhaald voor elk item in de lijst.

Het do / done -blok kan elke reeks bevatten van commando’s, zelfs een andere for -loop:

Lussen-binnen-lussen is een veelgebruikte constructie bij programmeren.Voor het grootste deel ga ik proberen problemen toe te wijzen die dit soort logica met zich meebrengen, aangezien het lastig kan zijn om tijdens het debuggen los te draaien.

Lees een bestand, regel voor regel, betrouwbaar met read-while

Omdat cat een bestand regel voor regel afdrukt, lijkt het volgende voor lus zinvol:

De opdrachtvervanging zorgt er echter voor dat cat woorden opsplitst in spatie. Als list-of-dirs.txt de volgende:

De uitvoer van de for -lus zal deze zijn:

Een read-while-lus zal de woorden binnen een regel behouden:

We kunnen ook uit het resultaat putten van een commando door het tussen <( en ):

Pipes and loops

Als u uit andere talen komt, zijn datastromen u wellicht niet bekendDat zijn ze in ieder geval voor mij, aangezien de syntaxis om ermee te werken veel directer en duidelijker is in Bash dan in Ruby of Python.

Als je echter nieuw bent in programmeren in welke taal dan ook, wat zou ook onduidelijk zijn, is hoe werken met datastromen anders is dan werken met loops.

Bijvoorbeeld het volgende fragment:

– produceert dezelfde output als deze lus:

En afhankelijk van je mentale model van dingen, lijkt het erop dat in beide voorbeelden elk woord, bijv. hello, world, wordt door een vertaalproces geleid (via tr) en vervolgens herhaald.

Buizen en filters

Zonder in de grondbeginselen van het Unix-systeem in te gaan, waarin een pijp fundamenteel anders werkt dan een lus, wil ik een mentale oplossing voorstellen:

Programma’s die pijpen van stdin en stdout kunnen meestal worden gerangschikt als filters, waarin een stroom gegevens gaat in een programma en komt uit in een ander formaat:

Voor taken die meer zijn dan alleen het transformeren van gegevens, van filter naar filter, denk erover na om een lus te gebruiken. Wat zou zo’n taak kunnen zijn? Gegeven een lijst met URL’s, download ze allemaal en e-mail de gedownloade gegevens, met een aangepaste hoofdtekst en onderwerp:

De gegevensinvoerbron, elke URL in urls.txt, wordt hier niet echt gefilterd. In plaats daarvan wordt voor elke URL een taak in meerdere stappen uitgevoerd.

Piping in read-while

Dat gezegd hebbende, een lus zelf kan worden geïmplementeerd als nog een filter onder de filters. Neem deze variant van de read-while-lus, waarin het resultaat van echo | grep regel voor regel wordt doorgesluisd naar de while lus, die wordt afgedrukt naar stdout met echo, die wordt omgeleid naar het bestand met de naam some.txt:

Dit is geen constructie die je misschien vaak moet doen, of helemaal niet, maar hopelijk versterkt het het gebruik van pijpleidingen in Unix.

Minder interactief programmeren

De veelvuldig gebruik van for loops en vergelijkbare constructies betekent dat we “voorbij de goede oude” dagen van typen in één regel van c gaan ommands en het laten uitvoeren direct nadat we op Enter hebben gedrukt. Het maakt niet uit hoeveel opdrachten we in een for -lus stoppen, er gebeurt niets totdat we op het done trefwoord klikken.

Schrijf een keer. Herhaal het dan

Met dat verlies van regel-voor-regel interactie met de shell, verliezen we het belangrijkste voordeel van de interactieve prompt: onmiddellijke feedback. En we hebben nog steeds alle nadelen: als we eerder een typefout maken in het blok met commando’s tussen do en done, moeten we beginnen overal.

Dus hier is hoe we dat verminderen:

Test uw code, geval voor geval

Een van de grootste fouten die beginners maken met for loops is dat ze denken dat een for loop hun probleem onmiddellijk oplost. Dus als wat ze moeten doen is het downloaden van 10.000 URL’s, maar ze kunnen “niet correct slechts één URL downloaden, ze denken dat het plaatsen van hun gebrekkige commando’s in een for -lus een stap in de goede richting is.

Naast dat dit een fundamenteel een verkeerd begrip van een for -lus, het praktische probleem is dat je nu je gebroken code 10.000 keer gebruikt, wat betekent dat je 10.000 keer zo lang moet wachten om erachter te komen dat je code, helaas, nog steeds kapot.

Dus doe alsof je “nog nooit hebt gehoord van for lussen. Stel je voor dat je alle 10.000 URL’s moet downloaden, één commando per keer. Kun je het commando schrijven om het voor de eerste URL te doen? Hoe zit het met de tweede? Als je er eenmaal redelijk zeker van bent dat er geen kleine syntaxisfouten zijn, dan is het tijd om na te denken over hoe je een algemeen patroon kunt vinden voor de 9.997 andere URL’s.

Schrijf scripts

De interactieve opdrachtregel is geweldig.Het was leuk om mee te beginnen, en het zal leuk zijn gedurende je hele computercarrière. Maar als je een grote taak voor je hebt, met meer dan tien regels code, dan is het tijd om die code in een shell-script. Vertrouw niet op je feilbare menselijke vingers om code feilloos over te typen.

Gebruik nano om aan loops te werken en sla ze op als shell scripts. Voor langere bestanden werk ik op de teksteditor van mijn computer (Sublime Text) en upload ik vervolgens naar de server.

Oefening met webscraping

Gewoon om de syntaxis en werking van de for-lus, hier is het denkproces van het veranderen van een routinetaak in een lus:

Voor de nummers 1 tot en met 10, gebruik curl om het Wikipedia-item voor elk nummer te downloaden en sla het op in een bestand met de naam “wiki-number-(whatever the number is).html

Op de ouderwetse manier

Met slechts 10 URL’s kunnen we een aantal variabelen instellen en vervolgens het a curl-commando 10 keer kopiëren en plakken, waarbij we wijzigingen aanbrengen in elke regel:

En wat denk je? Het werkt. Voor 10 URL’s is het geen slechte oplossing, en het is aanzienlijk sneller dan het op de ouderwetse manier te doen (doe het vanuit je webbrowser)

Herhaling verminderen

Zelfs zonder na te denken over een lus, kunnen we herhaling verminderen met behulp van variabelen: de basis-URL, en de basisbestandsnaam veranderen nooit, dus laten we die waarden aan variabelen toewijzen dat kan worden hergebruikt:

De for-loop toepassen

Op dit punt hebben we het patroon tot dusver vereenvoudigd zodat we kunnen zien hoe weinig veranderingen bij elke afzonderlijke taak zijn. Nadat we hebben geleerd over de for -loop, kunnen we deze zonder veel nadenken toepassen (we voegen ook een slaapopdracht toe zodat we pauzeren tussen webverzoeken).

Een list

In de meeste situaties is het maken van een for-loop eenvoudig; het is het maken van de lijst die zwaar werk kan zijn. Wat als we de pagina’s voor de nummers 1 tot en met 100 willen verzamelen? Dat is een hoop typen.

Maar als we onze luiheid laten dicteren ons denken kunnen we ons voorstellen dat het tellen van x tot y een inherent computationele taak lijkt. En dat is het ook, en Unix heeft hiervoor het seq -hulpprogramma:

Genereren van een lijst met niet-getallen voor iteratie

Veel repetitieve taken zijn niet zo eenvoudig als tellen van x tot y, en dus wordt het probleem hoe u een niet-lineaire lijst met items genereert? Dit is in feite de kunst van het verzamelen en beheren van gegevens. Maar laten we een eenvoudig scenario maken voor onszelf:

Voor tien van de 10-letterige (of meer) woorden die minstens één keer voorkomen in een kop op de huidige voorpagina van NYTimes.com, haal de Wiktionary-pagina voor dat woord op

We splitsen deze taak op in twee delen:

  1. Haal een lijst van tien 10 + -letterwoorden van koppen van nytimes.com
  2. Geef die woorden door aan onze for-loop

Stap 1: gebruik het hulpprogramma pup (of opdrachtregel-HTML-parser van uw keuze):

Stap 2 (ervan uitgaande dat de words variabele wordt doorgegeven):

Bekijk Softwa re Carpentry’s uitstekende gids voor for-loops in Bash

Leave a Reply

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *