next up previous contents
Nästa: 11.6 Strängoperatorer Upp: 11. Grundläggande Bashprogrammering Förra: 11.4 Hopp

11.5 Att hantera argument

Om man använder kommandot shift i ett Bashprogram, så tas det första argumentet bort. Det som förut var $2 blir då $1, det som var $3 blir $2 osv. Samtidigt minskar $# med ett. Kommandot shift 2 har samma verkan som två stycken shift. Dvs $# minskar med två, det som var $3 blir $1, det som var $4 blir $2 osv. På motsvarande sätt fungerar shift 3, shift 4 osv.

Följande program, som helt enkelt anger alla sina argument, ger ett exempel på hur shift kan användas.

#!/bin/bash
echo "Totalt $# argument."
while [ $# -gt 0 ]
do
  echo "Ett argument är $1."
  shift
done

Genom att köra programmet ovan några gånger ska vi demonstrera hur Bash hanterar argumenten. Vi minns sedan avsnitt 3.4 att oskyddade blank- och nyradtecken separerar argument.$ allaargument ole 'dole doff'
Totalt 2 argument.
Ett argument är ole.
Ett argument är dole doff.
$ allaargument "Hej och hå"
Totalt 1 argument.
Ett argument är Hej och hå.
$
Med hjälp av citationstecken kan man till och med ange den tomma textsträngen som ett argument.$ allaargument Ett tomt "" argument
Totalt 4 argument.
Ett argument är Ett.
Ett argument är tomt.
Ett argument är .
Ett argument är argument.
$
Separationen av argument sker efter eventuella omskrivningar (med undantag för jokeromskrivningar):$ NAMN="Göran Andersson"
$ allaargument namnet är $NAMN
Totalt 4 argument.
Ett argument är namnet.
Ett argument är är.
Ett argument är Göran.
Ett argument är Andersson.
$
Ovan skrevs $NAMN om till Göran Andersson, vilket räknas som två skilda argument. Om variabeln namn inte har getts något värde, så skrivs $namn om till en tom textsträng. Därför räknas $ODEFINIERAD som noll argument i följande exempel:$ allaargument Värdet är $ODEFINIERAD
Totalt 2 argument.
Ett argument är Värdet.
Ett argument är är.
$
Ibland är detta tvärtemot vad man vill. Hur får man Bash att uppfatta uttrycket $variabel som exakt ett argument även efter omskrivningen? Genom att omge det med citationstecken!$ allaargument namnet är "$NAMN"
Totalt 3 argument.
Ett argument är namnet.
Ett argument är är.
Ett argument är Göran Andersson.
$ allaargument Värdet är "$ODEFINIERAD"
Totalt 3 argument.
Ett argument är Värdet.
Ett argument är är.
Ett argument är .
$

 Om man slarvar vid hanteringen av argument, så kan man råka ut för märkliga spratt. Betrakta tex följande program, leta2:

#!/bin/bash
if grep -q $1 $2 ; then
  echo 'Ja, texten finns i filen.'
else
  echo 'Nej, texten finns inte i filen.'
fi

Det första argumentet ska vara en text, det andra ett filnamn. Programmet har till uppgift att ta reda på om texten finns i den angivna filen. Vi har gett grep flaggan -q för att slippa dess utskrift. Antag att filen textfil har följande innehåll:

Det första argumentet ska vara en text, det andra ett filnamn.
Programmet har till uppgift att
ta reda på om texten finns i den angivna filen.
Så här kan det gå när vi kör programmet leta2:$ leta2 'finns inte' textfil
Ja, texten finns i filen.
$
Nu påstår alltså programmet att texten ''finns inte'' förekommer i textfil! Orsaken till detta är att kommandot grep -q $1 $2 skrivs om till grep -q finns inte textfil. Alltså söker grep efter ordet ''finns'' i filerna inte och textfil. Ordet ''finns'' förekommer i filen textfil, så grep får slutstatus noll.

För att rätta till felet måste vi se till att grep betraktar värdet av $1 som exakt ett ord. Vi omger därför $1 med citationstecken. Hur går det om det andra argumentet, filnamnet, innehåller ett mellanslag? Dåligt. Värdet av $2 betraktas då av grep som flera filnamn, och programmet gör fel. Så här ska det se ut i stället:$ cat leta3
#!/bin/bash
if grep -q "$1" "$2" ; then
echo 'Ja, texten finns i filen.'
else
echo 'Nej, texten finns inte i filen.'
fi
$ cp textfil 'konstig fil'
$ leta3 finns 'konstig fil'
Ja, texten finns i filen.
$ leta3 'finns inte' 'konstig fil'
Nej, texten finns inte i filen.
$

Antag nu att vi vill skriva om leta3 så att det kan söka efter en viss text i flera olika filer. Dvs det första argumentet ska vara söktexten, de övriga ska vara filnamn. Då ska grep-kommandot ändras till grep -q "$1" "$2" "$3" ifall man anger två filnamn, till grep -q "$1" "$2" "$3" "$4" ifall man anger tre osv. Hur ska det gå till när vi inte vet hur många argumenten är? Bash tolkar $* som $1 $2 $3 osv fram till sista argumentet, men det duger inte här eftersom argumenten kan innehålla mellanslag. Istället ska vi använda parametern @. Den har samma värde som * utom när den omges av citationstecken: "$@" tolkas som "$1" "$2" "$3" osv, vilket är just vad vi behöver. Kommandot ska alltså skrivas grep -q "$@".

Vi använder följande program för att belysa diskussionen ovan:

#!/bin/bash
echo "Jag fick $# argument, men..."
allaargument $*
allaargument "$*"
allaargument "$@"

Så här går det när vi kör programmet:$ argumenttest ole 'dole doff'
Jag fick 2 argument, men...
Totalt 3 argument.
Ett argument är ole.
Ett argument är dole.
Ett argument är doff.
Totalt 1 argument.
Ett argument är ole dole doff.
Totalt 2 argument.
Ett argument är ole.
Ett argument är dole doff.
$
Programmet allaargument anropades tre gånger. Varje gång vidarebefordrades argumenten, fast det skedde på olika sätt. Först vidarebefordrades de som ole dole doff (allaargument fick då tre argument), sedan som "ole dole doff" (allaargument fick då ett argument) och till sist som "ole" "dole doff" (vilket gav två argument). Det är bara "$@" som bevarar argumenten precis som de var.

Man kan inte ge tex parametern 1 ett nytt värde genom en direkt tilldelning som om det varit en vanlig variabel. Däremot kan ett program ge sig själv de nya argumenten argument med kommandot set argument. Följande program använder set:

#!/bin/bash
allaargument "$@"
set Många "nya, fina" argument
allaargument "$@"

Låt oss prova programmet!$ argumenttest2 hejsan svejsan
Totalt 2 argument.
Ett argument är hejsan.
Ett argument är svejsan.
Totalt 3 argument.
Ett argument är Många.
Ett argument är nya, fina.
Ett argument är argument.
$

Programmet filinformation nedan använder set på ett intressantare sätt:

#!/bin/bash
set $(wc "$1")
echo "Filen $4 har $1 rader och är $3 byte lång."

Som argument ska man ange ett filnamn. Programmet talar om hur många rader och tecken filen innehåller.$ wc /etc/passwd
30 42 1291 /etc/passwd
$ filinformation /etc/passwd
Filen /etc/passwd har 30 rader och är 1291 byte lång.
$
Utskriften från wc $1 har fyra delar: först antal rader, sedan antal ord respektive tecken och till sist filnamnet. Dessa fyra delar sätts som argument, så att de kan plockas ut med $1, $2 osv.


next up previous contents
Nästa: 11.6 Strängoperatorer Upp: 11. Grundläggande Bashprogrammering Förra: 11.4 Hopp
Goran Andersson
1999-03-08