LX-SCR-INFO-for-loop
updated 7/27/2010 00:53

for loop

  1. herhaling van opdrachten

    Er zijn 3 herhalingsopdrachten in bash:

    • for
    • while
    • until

    Heel vaak willen we herhaaldelijk dezelfde routine uitvoeren, tot dat er (until) - of zolang er (while) - aan een bepaalde voorwaarde voldaan is. Bij for ligt het aantal uitvoeringen op voorhand vast of wordt het aantal ingegeven door een gebruiker. Herthalingsopdrachten noemen we een lus in het nederlands, en in het engels spreken we over een loop.

  2. for

    We kunnen de for loop gebruiken om te tellen, maar we gebruiken hem in bash vooral om de elementen van de output van een commando "woord" voor "woord" te selecteren.

    Bekijk het volgende voorbeeld:

    #!/bin/bash
    #
    # zipar: een utility om files in de huidige directory te archiveren
    # voorzien van een timestamp
    #
    timestamp=$(date +"%Y%m%d%H%M")
    for x in $(ls)
    do
        cp -v $x $x"_"$timestamp
        gzip -v $x"_"$timestamp
    done


    De output is dan als volgt:

    $ zipar
    `20081223_sendEmail.log' -> `20081223_sendEmail.log_201007231021'
    20081223_sendEmail.log_201007231021:     89.4% -- replaced with 20081223_sendEmail.log_201007231021.gz
    `2bye2quit' -> `2bye2quit_201007231021'
    2bye2quit_201007231021:     35.3% -- replaced with 2bye2quit_201007231021.gz
    `addressbook_sorted.csv' -> `addressbook_sorted.csv_201007231021'
    addressbook_sorted.csv_201007231021:     57.7% -- replaced with addressbook_sorted.csv_201007231021.gz

    $ ls -l
    total 564
    -rw-r--r-- 1 bert bert 139224 2008-12-23 01:02 20081223_sendEmail.log
    -rw-r--r-- 1 bert bert  14844 2010-07-23 10:21 20081223_sendEmail.log_201007231021.gz
    -rwxr-xr-x 1 bert bert    232 2010-07-22 23:51 2bye2quit
    -rwxr-xr-x 1 bert bert    199 2010-07-23 10:21 2bye2quit_201007231021.gz
    -rw-r--r-- 1 bert bert  15689 2008-12-23 00:10 addressbook_sorted.csv
    -rw-r--r-- 1 bert bert   6694 2010-07-23 10:21 addressbook_sorted.csv_201007231021.gz

  3. Oefeningen:

    1. Wijzig in het hogere voorbeeld, zipar, de for instructie als volgt:

      for x in *

      werkt alles nog zoals in het voorbeeld?

    2. Werkt zipar ook met $(ls -l) ?

    3. In het voorbeeld "zipar" voeg je de volgende elementen toe:

      • geef het pad waarin moet worden garchiveerd als parameter mee, indien geen pad wordt meegegeven wordt de huidige directory ge'zipar'd

      • test of er een archiefdirectory bestaat met als naam ziparchief in de directory waarin zipar wordt uitgevoerd, en plaats hierin de gezipte bestanden. Als de directory niet bestaat moet ze gemaakt worden.

      • voeg seconden toe in de timestamp

      • vervang de underscore in de nieuwe filenaam door een .

      • Vraagje: waarom heeft het geen zin om de gezipte bestanden achteraf nog eens te tarren?

    4. Hoe lees je met een eenvoudige for loop /etc/passwd om hem dan regel per regel terug op scherm te brengen met een echo? Gebeuren er rare dingen? Waarom? Zijn hier oplossingen voor?

    5. Maak een bestand met als naam "leden" (met op elke regel een naam, geslacht en taalcode) van de vorm:
      ludo m n

      claire v n
      luc m f
      gregory m e
      marleen v f
      corsendonk donker

      Schrijf een script welkom4 dat dit bestand inleest met een for loop, en druk telkens, regel per regel, een correcte aanspreektitel af (in de juiste taal), gevolgd door de naam. (vb: Dag Meneer ludo <nieuwe regel> Dag Mevrouw claire <nieuwe regel> Bonjour Monsieur Luc ...)
      Tip: Gebruik een extra tel-variabele om te kijken waar de volgende regel begint.

  4. Data Lijsten:

    Een for loop kan ook data halen uit een variabele met spaties als scheidingsteken:

    #!/bin/bash
    #
    # maanden: haalt de maanden van het jaar uit een data-variabele
    #
    demaanden="januari februari maart april mei juni juli augustus september oktober november december"           # alles op één regel
    for x in $demaanden
    do
            echo $x
    done

  5. Array

    Met een array variabele kun je bijvoorbeeld maandnummers mappen op hun benaming in om het even welke taal. In het volgende voorbeeld is MAANDEN een array.

    array syntax:

    Door de dubbele quotes in het vorige voorbeeld (data lijsten)te vervangen door haakjes, wordt de variabele een array van strings.

    MAANDEN=(0 januari februari maart april mei juni juli augustus september oktober november december)

    Dat betekent dat je de individuele elementen kunt oproepen met een index. In het onderstaande voorbeeld zou dat betekenen dat de waarde van ${MAANDEN[1]} gelijk is aan januari.

    echo ${MAANDEN[1]}
    januari

    het script in detail:

    De uitleg van het script is groter dan het script zelf:

    Een array begint altijd met index 0. Gezien er geen met nul genummerde maand bestaat moeten we de array voorzien van een nutteloze string (in ons geval zero) op de eerste plaats om een één op één mapping te bekomen.

    Verder wordt er in het voorbeeld gebruik gemaakt van het commando stat. In de manpagina's kun je lezen wat stat nu precies doet, en wat het doet in dit voorbeeld met de optie -c en parameter %y.

    Over awk vindt je in een volgend hoofdstuk meer informatie. In dit geval wordt awk losgelaten op de output van stat -c %y. Met de optie -F wordt het scheidingsteken '-' gekozen om te verdelen in kolommen. '{print $2}' zorgt ervoor dat de tweede kolom wordt afgedrukt (in dit geval wordt die in MAANDNUMMER opgeslagen)

    #!/bin/bash
    #
    # maandnummer
    #
    MAANDEN=(zero januari februari maart april mei juni juli augustus september oktober november december)

    for BESTAND in $(find . -type f)
    do
          MAANDNUMMER=`stat -c "%y" $BESTAND | awk -F- '{print $2}'`
          MAANDNAAM=${MAANDEN[$MAANDNUMMER]}
          echo $BESTAND $MAANDNUMMER $MAANDNAAM
    done

    Met als typische output:

    $ maandnummer
    ./space 06 juni
    ./addressbook_sorted.csv 12 december
    ./index.html 02 februari
    ./dagjij 06 juni
    ./zipar 07 juli
    ./casemenu 06 juni
    ./showparams 06 juni
    ./ifcolor 06 juni
    ./wegermee 06 juni
    ./piccopy 07 juli
    ./tien 07 juli
    ./sysinfo2html.bash 02 februari
    ./mijnsysteem 06 juni
    ./dwf 06 juni


  6. Tellen:

    Het script "tien" voor mensen die graag tellen.
    for haalt getallen uit een lijst, die getallen genereren we met seq

    #!/bin/bash
    #
    # tien: voegt 10 keer het karakter "x" toe aan een variabele
    #
    aantal="10"
    karakter="x"
    for x in $(seq 1 $aantal)
    do
              string=$string$karakter
    done

    echo $string


    Om het script "tien" interactief te maken, hebben we het als volgt uitgebreid:

    #!/bin/bash

    #
    # eentjes: druk een aantal karakters af, afhankelijk van de input
    #
    echo Welk karakter moeten we herhalen?
    read karakter
    # indien je niets ingeeft wordt karakter het cijfer 1
    if [ "$karakter" = "" ]; then karakter="1"; fi
    echo Hoeveel keer moeten we dit herhalen?
    read aantal
    # indien je niets ingeeft wordt het aantal op 0 gezet
    if [ "$aantal" = "" ]; then aantal="0"; fi
    # indien je iets hebt ingegeven dat geen geldig getal is wordt aantal op 0 gezet
    # we vergelijken of $aantal groter is dan 0
    # en dat werkt in deze syntax alleen met integers

    if [ "$aantal" -gt "0" 2>/dev/null ]
    then
            #debug  echo $aantal is een correct getal
            # $aantal is inderdaad een getal
            # maar we doen niets: deze test werkt alleen met -gt en niet met ! -gt

            # then verwacht echter altijd dat we iets doen,
            # we zetten daarom een variabele gelijk aan niets

            doeniets=""
    else
            echo $aantal is geen correct getal
            aantal="0"
    fi
    #debug  echo $karakter, $aantal
    for x in $(seq 1 $aantal)
    do
    string=$string$karakter
    done

    echo $string

  7. Meer Oefeningen:

    1. Breng een alfabetisch gesorteerde lijst van de users die kunnen inloggen op scherm.

    2. Controlleer de grootte van de homedir van elke user. Indien die groter is dan 2MB echo dan de username op scherm.