 
original in en Katja en Guido Socher
en to nl Floris Lambrechts
Katja is de Duitse hoofdredactrice van LinuxFocus. Ze houdt van Tux, film & fotografie en de zee. Haar homepage vind je hier.
Guido is al lang een Linux-fan omdat het gemaakt wordt door eerlijke en open mensen. Dat is één van de redenen waarom we het open source noemen. Zijn homepage staat op linuxfocus.org/~guido.
 
#!/bin/shDe tekens #! vertellen het systeem dat de tekst die volgt (in dit geval /bin/sh) een programma is dat de rest van het bestand zal uitvoeren.
chmod +x bestandsnaamJe voert het vervolgens uit door te typen: ./bestandsnaam
varnaam=waardeOm de waarde uit te lezen plaats je een dollarteken voor z'n naam:
    #!/bin/sh
    # assign a value:
    a="hello world"
    # now print the content of "a":
    echo "A is:"
    echo $a
Typ deze tekst in je editor en bewaar het bijvoorbeeld als "eerste".
Dan maak je het uivoerbaar (chmod +x eerste in de shell) en voer je het uit
(./eerste).    A is:
    hello world
Soms kun je variabelen verwarren met de rest van de tekst:    num=2
    echo "dit is de $numde"
Dit geeft niet, zoals je misschien zou willen, "dit is de 2de" maar wel 
"dit is de " omdat de shell hier gaat zoeken naar een variable met de naam
"numde" die nog niet bestaat en dus leeg is. Om duidelijk te maken dat we
de variable num bedoelen, zetten we er haakjes rond:     num=2
    echo "dit is de ${num}de"
Deze keer is het wel goed: als uitvoer krijgen we "dit is de 2de".| Syntax v.h. commando | Doel | 
|---|---|
| echo "een tekst" | schrijft een tekst op het scherm | 
| ls | geeft een overzicht van de aanwezige bestanden | 
| wc -l bestand wc -w bestand wc -c bestand | telt het aantal regels in een bestand of telt het aantal woorden of telt het aantal tekens | 
| cp bronbestand doelbestand | kopieert het bronbestand naar het doelbestand | 
| mv oudnaam nieuwnaam | hernoemt of verplaatst het bestand | 
| rm bestand | verwijdert een bestand | 
| grep 'patroon' bestand | kijkt of het patroon voorkomt in een bestand Voorbeeld: grep 'zoekdezetekst' bestand.txt | 
| cut -b kolnum bestand | filtert data uit een bestand met vaste kolombreedte Voorbeeld: toon teken 5 t/m 9 cut -b5-9 bestand.txt Niet te verwarren met "cat", dat iets heel anders doet | 
| cat bestand.txt | schrijft bestand.txt naar stdout (standard output, het sherm dus) | 
| file eenbestand | geeft aan welk type bestand eenbestand is | 
| read var | vraagt de gebruiker om invoer en slaat het op in een variabele (var) | 
| sort bestand.txt | sorteert lijnen in bestand.txt | 
| uniq | verwijdert identieke lijnen, in combinatie met sort omdat uniq 
      enkel opeenvolgende lijnen bekijkt Voorbeeld: sort bestand.txt | uniq | 
| expr | doet aan wiskunde in de shell Voorbeeld: tel 2 op bij 3 expr 2 "+" 3 | 
| find | zoekt bestanden Voorbeeld: zoeken op naam: find . -name bestandsnaam -print Dit commando heeft vele mogelijkheden, spijtig genoeg teveel om in dit artikel te bespreken. | 
| tee | schrijft data naar stdout (het scherm) én naar een bestand Meestal op deze manier: eencommando | tee hetbestand Het schrijft de uitvoer van eencommando naar het scherm en naar hetbestand. | 
| basename bestand | geeft enkel de bestandsnaam van het gegeven bestand, het verwijdert het directory-pad Voorbeeld: basename /bin/tux geeft gewoon tux | 
| dirname bestand | geeft enkel de naam van de directory, niet die van het bestand Voorbeeld: dirname /bin/tux geeft enkel /bin | 
| head bestand | print de eerste regels van het bestand op het scherm | 
| tail bestand | print de laatste regels van het bestand op het scherm | 
| sed | in essentie is sed een programma voor het zoeken en vervangen van tekst.
      Het leest tekst van de standaard invoer (bijv. uit een pipe) en schrijft het resultaat
      naar stdout (meestal het scherm). Het zoekpatroon is een reguliere expressie (meer info in
      de referenties onderaan). Dit zoekpatroon mag je niet verwarren met de wildcards van de shell.
      Om in een bestand de tekst "linuxfocus" te vervangen door "LinuxFocus" doe je: cat bestand.txt | sed 's/linuxfocus/LinuxFocus/' > nieuwbestand.txt Dit vervangt de eerste "linuxfocus" in een regel door "LinuxFocus". Als de tekst meerdere keren in 1 regel voorkomt: cat bestand.txt | sed 's/linuxfocus/LinuxFocus/g' > nieuwbestand.txt | 
| awk | awk wordt meestal gebruikt om bepaalde velden uit een tekstregel te lezen.
      Standaard worden de velden onderscheiden door een spatie. Een andere
      field separator kan je instellen met de optie -F.  cat bestand.txt | awk -F, '{print $1 "," $3 }' 
Hier gebruiken we de komma (,) om de velden te onderscheiden en printen we het eerste en derde
      ($1 $3) veld. Als bestand.txt regels heeft zoalsAdam Bor, 34, India Kerry Miller, 22, USAdan geeft dit commando: Adam Bor, India Kerry Miller, USAAwk kan nog veel meer maar dit is een veel voorkomend gebruik.@@@ | 
grep "hello" bestand.txt | wc -lzoekt alle regels met de tekst "hello" in bestand.txt en telt vervolgens deze regels.
find . -mtime -1 -type f -printzoekt alle bestanden die in de laatste 24 uur veranderd zijn (-mtime -2 zou 48 uur zijn). Als je nu al deze bestanden in een tar-archief wilt opslaan (archief.tar) dan doe je dat zo:
tar xvf archief.tar bestand1 bestand2 ...In plaats van dit allemaal zelf te typen, kun je beide opdrachten (find en tar) combineren met backticks. Tar zal dan alle bestanden archiveren die het find commando gevonden heeft:
#!/bin/sh
# De aanhalingstekens zijn backticks (`), geen gewone (') ! 
tar -zcvf gewijzigd.tar.gz `find . -mtime -1 -type f -print`
if ....; then .... elif ....; then .... else .... fiMeestal gebruik je een speciaal commando (test genaamd) om de if-statements te controleren. Je kunt er stukken tekst (strings) mee vergelijken, testen of een bepaald bestand bestaat, of het leesbaar is, enz.
[ -f "eenbestand" ] : Test of eenbestand wel echt een bestand is. [ -x "/bin/ls" ] : Test of /bin/ls bestaat en of het uitvoerbaar is. [ -n "$var" ] : Test of de variabele $var wel een waarde heeft. [ "$a" = "$b" ] : Test of de variabelen "$a" en "$b" hetzelfde zijn.Doe een keer "man test" en je krijgt een lange lijst met alle soorten testopdrachten voor vergelijkingen en bestanden.
#!/bin/sh if [ "$SHELL" = "/bin/bash" ]; then echo "your login shell is the bash (bourne again shell)" else echo "your login shell is not bash but $SHELL" fiDe variabele $SHELL bevat de naam van de login-shell en dat is wat we testen door het te vergelijken met de string "/bin/bash".
[ -f "/etc/shadow" ] && echo "This computer uses shadow passwords"De && kun je gebruiken als een kort if-statement. De rechterkant wordt enkel uitgevoerd als de linkerkant 'waar' is. Je kunt het bekijken als een AND. In dit voorbeeld: "Het bestand /etc/shadow bestaat AND de opdracht wordt uitgevoerd". De OR operator kun je ook gebruiken, hij wordt geschreven als '||'. Een voorbeeld:
#!/bin/sh
mailfolder=/var/spool/mail/james
[ -r "$mailfolder" ] || { echo "Can not read $mailfolder" ; exit 1; }
echo "$mailfolder has mail from:"
grep "^From " $mailfolder
Het script test eerst of het de mailfolder wel kan lezen.
Zo ja, dan print het al de lijnen die "From" bevatten. Als het de folder niet
kan lezen, dan wordt de opdracht die na de OR staat uitgevoerd. Je kunt deze
code zo bekijken "De mailfolder moet leesbaar zijn, of anders stoppen we het script".
Het probleem is dat er na de OR slechts 1 commando mag staan, terwijl we er
twee nodig hebben.
case ... in ...) doe hier iets;; esacWe bekijken een voorbeeld. Het commando
file gaat na
tot welk type een bepaald bestand hoort:
file lf.gzgeeft:
lf.gz: gzip compressed data, deflated, original filename, last modified: Mon Aug 27 23:09:18 2001, os: UnixWe gaan dit nu gebruiken om een script te schrijven: smartzip. Smartzip kan bzip2, gzip en zip archieven automatisch uitpakken.
#!/bin/sh
ftype=`file "$1"`
case "$ftype" in
"$1: Zip archive"*)
    unzip "$1" ;;
"$1: gzip compressed"*)
    gunzip "$1" ;;
"$1: bzip2 compressed"*)
    bunzip2 "$1" ;;
*) error "File $1 can not be uncompressed with smartzip";;
esac
smartzip
articles.zipbash
en zeer handig voor interactieve toepassingen. De gebruiker kan een keuze maken
uit een lijst van verschillende waardes: select var in ... ; do break done .... nu kun je $var gebruiken ....Een voorbeeld:
#!/bin/sh
echo "What is your favourite OS?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do
        break
done
echo "You have selected $var"
Hier is wat dit script doet:What is your favourite OS? 1) Linux 2) Gnu Hurd 3) Free BSD 4) Other #? 1 You have selected LinuxIn de shell heb je volgende lus-statements:
while ...; do .... doneDe while-lus zal actief blijven zolang de expressie waarop getest wordt 'true' is. Met het sleutelwoord "break" kun je de lus altijd verlaten. Met "continue" sla je het vervolg van de huidige lus over en begin je meteen aan de volgende iteratie.
for var in ....; do .... doneHet volgende zal bijv. de letters A tot C op het scherm printen:
#!/bin/sh for var in A B C ; do echo "var is $var" doneEen meer bruikbaar script, showrpm genaamd, toont een samenvatting van de inhoud van een aantal RPM packages.
#!/bin/sh
# list a content summary of a number of RPM packages
# USAGE: showrpm rpmfile1 rpmfile2 ...
# EXAMPLE: showrpm /cdrom/RedHat/RPMS/*.rpm
for rpmpackage in $*; do
  if [ -r "$rpmpackage" ];then
    echo "=============== $rpmpackage =============="
    rpm -qi -p $rpmpackage
  else
    echo "ERROR: cannot read file $rpmpackage"
  fi
done
Je ziet alweer een nieuwe variabele, deze keer $*. Die bevat al de
argumenten van de commandoregel. Als je showrpm openssh.rpm w3m.rpm webgrep.rpm
#!/bin/sh echo *.jpgDit zal worden omgezet naar "mail.jpg tux.jpg".
#!/bin/sh echo "*.jpg" echo '*.jpg'Dit geeft twee keer "*.jpg" op het scherm.
#!/bin/sh echo $SHELL echo "$SHELL" echo '$SHELL'Dit geeft:
/bin/bash /bin/bash $SHELLTenslotte is er nog de mogelijkheid de speciale betekenis van bepaalde karakters op te heffen door er een backslash voor te zetten:
echo \*.jpg echo \$SHELLDit geeft:
*.jpg $SHELLHere-documents
#!/bin/sh
# we have less than 3 arguments. Print the help text:
if [ $# -lt 3 ] ; then
cat <<HELP
ren -- renames a number of files using sed regular expressions
USAGE: ren 'regexp' 'replacement' files...
EXAMPLE: rename all *.HTM files in *.html:
  ren 'HTM$' 'html' *.HTM
HELP
  exit 0
fi
OLD="$1"
NEW="$2"
# The shift command removes one argument from the list of
# command line arguments.
shift
shift
# $* contains now all the files:
for file in $*; do
    if [ -f "$file" ] ; then
      newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"`
      if [ -f "$newfile" ]; then
        echo "ERROR: $newfile exists already"
      else
        echo "renaming $file to $newfile ..."
        mv "$file" "$newfile"
      fi
    fi
done
Dit is het meest complexe script tot nu toe. Het eerste
if-statement test of we tenminste drie opties hebben meegegeven
(de speciale variabele $# bevat het aantal argumenten). Is dat niet het geval,
dan sturen we de helptekst naar het cat-commando,
die het op het scherm zet. Nadat de tekst weergegeven is, sluiten we het
script af. Als er daarentegen wél drie of meer argumenten zijn, wijzen
we het eerste toe aan de variabele OLD en het tweede aan de variabele NEW.
Dan 'shiften' we de parameters twee keer om het derde argument in de eerste
positie van $* te krijgen. Met $* beginnen we de for-lus. Elk van
de argumenten in $* wordt nu één voor één
toegewezen aan de variabele $file. Hier testen we eerst of het bestand bestaat
en dan maken we de nieuwe bestandsnaam door zoeken en vervangen te doen met
sed. De backticks gebruiken we om het resultaat toe te wijzen
aan de variabele newfile. Nu hebben we alles wat we nodig hebben: de oude
bestandsnaam en de nieuwe. Met de mv opdracht hernoemen we
vervolgens het bestand.
functienaam()
{
 # binnnen de functie is $1 het eerste argument gegeven aan de functie
 # $2 het tweede ...
 code
}
Je moet functies "declareren" aan het begin van het script voordat je ze
kunt gebruiken.
#!/bin/sh
# vim: set sw=4 ts=4 et:
help()
{
    cat <<HELP
xtitlebar -- change the name of an xterm, gnome-terminal or kde konsole
USAGE: xtitlebar [-h] "string_for_titelbar"
OPTIONS: -h help text
EXAMPLE: xtitlebar "cvs"
HELP
    exit 0
}
# in case of error or if -h is given we call the function help:
[ -z "$1" ] && help
[ "$1" = "-h" ] && help
# send the escape sequence to change the xterm titelbar:
echo -e "\033]0;$1\007"
#
Het is een goede gewoonte om altijd goede help op te nemen in je scripts.
Dat maakt het mogelijk voor anderen (en jezelf) om later het script snel
te verstaan en aan te passen.
#!/bin/sh
help()
{
  cat <<HELP
This is a generic command line parser demo.
USAGE EXAMPLE: cmdparser -l hello -f -- -somefile1 somefile2
HELP
  exit 0
}
while [ -n "$1" ]; do
case $1 in
    -h) help;shift 1;; # function help is called
    -f) opt_f=1;shift 1;; # variable opt_f is set
    -l) opt_l=$2;shift 2;; # -l takes an argument -> shift by 2
    --) shift;break;; # end of options
    -*) echo "error: no such option $1. -h for help";exit 1;;
    *)  break;;
esac
done
echo "opt_f is $opt_f"
echo "opt_l is $opt_l"
echo "first arg is $1"
echo "2nd arg is $2"
Probeer het maar! Je kunt het uitvoeren met bijv.: cmdparser -l hello -f -- -eenbestand1 eenbestand2Het resultaat is dan
opt_f is 1 opt_l is hello first arg is -eenbestand1 2nd arg is eenbestand2Hoe werkt het? Het gaat alle argumenten af en bekijkt ze met een
case-
statement. Als het een geldige optie is, wordt die opgeslagen in een variabele
en 'shift' het de commandoregel één positie. De Unix-conventie
is dat opties (dingen die beginnen met een minteken) als eerste komen. Je kunt
duidelijk maken dat het einde van de opties bereikt is door een dubbel
minteken (--) te plaatsen. Dit is bijvoorbeeld nodig als je met egrep
gaat zoeken naar een string met een minteken in: Zoek naar -xx- in bestand f.txt: grep -- -xx- f.txtOnze optie-parser kan ook omgaan met '--' zoals je ziet in de code hierboven.
cp framework.sh mijnscripten begin dan aan het schrijven van je eigen functies.
expr-commando.
#!/bin/sh
# vim: set sw=4 ts=4 et:
help()
{
  cat <<HELP
b2h -- convert binary to decimal
USAGE: b2h [-h] binarynum
OPTIONS: -h help text
EXAMPLE: b2h 111010
will return 58
HELP
  exit 0
}
error()
{
    # print an error and exit
    echo "$1"
    exit 1
}
lastchar()
{
    # return the last character of a string in $rval
    if [ -z "$1" ]; then
        # empty string
        rval=""
        return
    fi
    # wc puts some space behind the output this is why we need sed:
    numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `
    # now cut out the last char
    rval=`echo -n "$1" | cut -b $numofchar`
}
chop()
{
    # remove the last character in string and return it in $rval
    if [ -z "$1" ]; then
        # empty string
        rval=""
        return
    fi
    # wc puts some space behind the output this is why we need sed:
    numofchar=`echo -n "$1" | wc -c | sed 's/ //g' `
    if [ "$numofchar" = "1" ]; then
        # only one char in string
        rval=""
        return
    fi
    numofcharminus1=`expr $numofchar "-" 1` 
    # now cut all but the last char:
    rval=`echo -n "$1" | cut -b 0-${numofcharminus1}`
}
    
while [ -n "$1" ]; do
case $1 in
    -h) help;shift 1;; # function help is called
    --) shift;break;; # end of options
    -*) error "error: no such option $1. -h for help";;
    *)  break;;
esac
done
# The main program
sum=0
weight=1
# one arg must be given:
[ -z "$1" ] && help
binnum="$1"
binnumorig="$1"
while [ -n "$binnum" ]; do
    lastchar "$binnum"
    if [ "$rval" = "1" ]; then
        sum=`expr "$weight" "+" "$sum"`
    fi
    # remove the last position in $binnum
    chop "$binnum"
    binnum="$rval"
    weight=`expr "$weight" "*" 2`
done
echo "binary $binnumorig is decimal $sum"
#
Het algoritme in dit script bekijkt de decimale waarde van alle cijfers, te
beginnen van rechts (1,2,4,8,16,..), en voegt het toe aan de som als het
cijfer in kwestie een 1 is. Dus "10" wordt: lastchar.
Dit maakt gebruikv an wc -c om het aantal karakters in een string
te tellen en knipt er dan het laatste uit. De chop-functie
volgt hetzelfde principe, maar verwijdert eerst het laatste karakter en geeft dan
alle voorgaande weer.
#!/bin/sh
# vim: set sw=4 ts=4 et: 
ver="0.1"
help()
{
    cat <<HELP
rotatefile -- rotate the file name 
USAGE: rotatefile [-h]  filename
OPTIONS: -h help text
EXAMPLE: rotatefile out
This will e.g rename out.2 to out.3, out.1 to out.2, out to out.1
and create an empty out-file
The max number is 10
version $ver
HELP
    exit 0
}
error()
{
    echo "$1"
    exit 1
}
while [ -n "$1" ]; do
case $1 in
    -h) help;shift 1;;
    --) break;;
    -*) echo "error: no such option $1. -h for help";exit 1;;
    *)  break;;
esac
done
# input check:
if [ -z "$1" ] ; then
 error "ERROR: you must specify a file, use -h for help" 
fi
filen="$1"
# rename any .1 , .2 etc file:
for n in  9 8 7 6 5 4 3 2 1; do
    if [ -f "$filen.$n" ]; then
        p=`expr $n + 1`
        echo "mv $filen.$n $filen.$p"
        mv $filen.$n $filen.$p
    fi
done
# rename the original file:
if [ -f "$filen" ]; then
    echo "mv $filen $filen.1"
    mv $filen $filen.1
fi
echo touch $filen
touch $filen
Hoe werkt het? Eerst controleren we of de gebruiker een bestandsnaam
heeft ingegeven. Dan gaan we in een lus die van 9 aftelt tot 1. Bestand 9
wordt hernoemd tot 10, 8 tot 9 en zo verder. Na deze lus noemen we het
originele bestand 1 en maken we een leeg bestand aan met de originele naam.
sh -x foutscriptDit zal het script uitvoeren en alle opdrachten tonen die worden uitgevoerd, samen met de variabelen en de resultaten van wildcards.
sh -n je_scriptAls je geen uitvoer krijgt, dan zijn er geen syntax-fouten gevonden.