![Übergeben von Argumenten an ein Skript](https://rvso.com/image/913177/%C3%9Cbergeben%20von%20Argumenten%20an%20ein%20Skript.png)
Ich besuche den Kurs „Linux Essentials“ und kam gut voran, bis ich zum Kapitel „Scripting“ kam. Ich verstehe diese Konzepte einfach nicht. Ich frage mich, ob jemand das Folgende in extrem einfache Begriffe aufschlüsseln oder mir eine bessere Referenz zum Erlernen nennen kann. Ich verwende derzeit den Lehrplan von Netacad.
Aus dem Lehrbuch (mit geringfügigen Formatierungsänderungen):
Zusätzlich zu den von Ihnen festgelegten Variablen gibt es einige spezielle Variablen. Sie können Argumente an Ihr Skript übergeben:
#!/bin/bash echo "Hello $1"
Ein Dollarzeichen gefolgt von einer Zahl N entspricht dem N-ten Argument, das an das Skript übergeben wird. Wenn Sie das obige Beispiel aufrufen,
./test.sh
lautet die Ausgabe Hello Linux. Die$0
Variable enthält den Namen des Skripts selbst.Nach der Ausführung eines Programms (egal ob es sich um eine Binärdatei oder ein Skript handelt) wird ein Exit-Code zurückgegeben, der eine Ganzzahl zwischen 0 und 255 ist. Sie können dies anhand der
$?
Variablen testen, um zu sehen, ob der vorherige Befehl erfolgreich ausgeführt wurde.
Ich verstehe, wie man Variablen zuweist und wie sie damit funktionieren, $
aber die ganze Sache mit $0
und $1
– ich verstehe es einfach nicht.
Ich wäre für jede Hilfe sehr dankbar.
Antwort1
Die Beschreibung im Buch ist falsch (oder es fehlt zumindest etwas). Um das Skript dazu zu bringen, „Hallo Linux“ auszugeben, führen Sie es wie folgt aus:
./test.sh Linux
Wenn Sie es nur mit ausführen ./test.sh
, wird nur „Hello “ ausgegeben, da kein erstes Argument vorhanden war und $1
es nicht definiert ist. Nehmen wir andererseits an, Sie haben es folgendermaßen ausgeführt:
./test.sh foo bar baz
$0
dann wäre innerhalb des Skripts „./test.sh“, $1
wäre „foo“, $2
wäre „bar“ und $3
wäre „baz“.
Beachten Sie hierzu $?
den folgenden Skriptausschnitt:
ls nonexistentfile.txt
echo "The exit status of ls was: $?"
echo "The exit status of echo (the first one) was: $?"
Beim Ausführen wird etwa Folgendes ausgegeben:
ls: nonexistentfile.txt: No such file or directory
The exit status of ls was: 1
The exit status of echo (the first one) was: 0
Der ls
Befehl kann nonexistentfile.txt nicht auflisten (weil es nicht existiert), also gibt er eine entsprechende Fehlermeldung aus und beendet sich mit einem Status ungleich Null, um anzuzeigen, dass etwas schiefgelaufen ist. Der erste echo
Befehl gibt diesen Beendigungsstatus ( $?
) aus, und da er dies erfolgreich tut, wird er mit einem Status von Null beendet. Wenn der zweite echo
Befehl ausgeführt wird, erhält er den Status $?
vom ersten echo
Befehl und gibt daher „0“ aus.
Übrigens verwenden viele Befehle nur Exit-Status von 0 (Erfolg) oder 1 (irgendeine Art von Fehler), aber einige verwenden unterschiedliche Fehlerstatus, um genau anzugeben, was schief gelaufen ist. Hier ist ein Auszug aus demcurl
Handbuchseite:
EXIT CODES
There are a bunch of different error codes and their corresponding
error messages that may appear during bad conditions. At the time of
this writing, the exit codes are:
1 Unsupported protocol. This build of curl has no support for this
protocol.
2 Failed to initialize.
3 URL malformed. The syntax was not correct.
...
88 FTP chunk callback reported error
89 No connection available, the session will be queued
90 SSL public key does not matched pinned public key
91 Invalid SSL certificate status.
92 Stream error in HTTP/2 framing layer.
...damit ein verwendetes Skript curl
herausfinden kann $?
, was schiefgelaufen ist, und je nach Problem unterschiedlich reagieren kann.
Antwort2
$0
ist der Name, den Sie zum Ausführen des Skripts verwenden. $1
, $2
, usw. sind die Positionsparameter des Skripts, die die Werte der Befehlszeilenargumente enthalten, die Sie beim Ausführen des Skripts übergeben haben.
AlsGordon Davisson sagte, muss der Autor des Buches gemeint haben, dass beim Ausführen ./test Linux
ausgegeben wird Hello Linux
. Wenn Sie das tun, ./test
geht in den speziellen Parameter 0
und Linux
in den ersten Positionsparameter 1
. Das Skript erweitert diesen ersten Positionsparameter, indem es ihm ein Dollarzeichen ( $1
) voranstellt, genau wie Sie es mit Variablen tun. Wenn Sie stattdessen ausgeführt hätten ./test Hello Linux for Human Beings
, würde im Skript zu , zu , zu und zu $1
erweitert .Linux
$2
for
$3
Human
$4
Beings
Sie können ein einfaches Skript schreiben, um dies auszuprobieren:
#!/bin/bash
echo "\$0 expands to '$0'."
echo "\$1 expands to '$1'."
echo "\$2 expands to '$2'."
echo "\$3 expands to '$3'."
(Machen Sie so weit weiter, wie Sie wollen. Für Positionsparameter, die höher sind als 9
, verwenden Sie die ${
}
Form der Parametererweiterung, z. B. erweitern Sie, 10
indem Sie schreiben ${10}
. In Skripten, die mit vielen Positionsparametern arbeiten, @
wird häufig der spezielle Parameter verwendet, um Wiederholungen zu vermeiden, aber Sie können das vorerst ignorieren, wenn Sie möchten.)
Versuchen Sie, das in einer Datei zu speichern und die Datei als ausführbar zu markieren. Dies können Sie tun, indem Sie ausführen, chmod +x simple
wobei durch den Namen der Datei ersetzt wird, falls dieser abweicht. Anschließend können Sie es mit Befehlen wie , , , usw. simple
ausführen ../simple
./simple foo
./simple foo bar
Sie werden feststellen, dass Positionsparameter, die den nicht übergebenen entsprechen, zu einer leeren Zeichenfolge erweitert werden, wenn weniger als drei Befehlszeilenargumente übergeben werden. Das passiert, wenn Sie versuchen, einen Shell-Parameter zu erweitern, der nicht definiert ist. Sie werden außerdem feststellen, dass bei der Übergabe mehrerer Befehlszeilenargumente die nach dem dritten nicht verwendet werden. Das ist wahrscheinlich das, was Sie erwarten würden, da das Skript überhaupt nicht auf sie verweist.
Versuchen Sie nun, auszuführen ./simple *
. Die Shell erweitert *
alle Dateinamen im aktuellen Verzeichnis außer denen, die mit beginnen .
. Daher werden drei davon als die ersten drei Positionsparameter angezeigt (oder weniger, wenn es nicht so viele sind). Sie können versuchen, es mit anderen Shell-Erweiterungen auszuführen, beispielsweise ./simple {5..10}
.
Sie können Befehlszeilenargumente mit Leerzeichen übergeben, indem Sie sie in Anführungszeichen setzen. Versuchen Sie es beispielsweise mit ./simple 'foo bar' baz
. Beachten Sie, dass sich dies $1
auf foo bar
dieses Mal erweitert und nicht nur auf foo
.
Denn die Schale leistetdiverse Erweiterungenist es nicht immer offensichtlich, wie viele Befehlszeilenargumente Sie an einen Befehl übergeben. Eine einfache Möglichkeit, um zu sehen, was jedes Argument sein wird, besteht darin, den Befehl durch zu ersetzen printf '[%s]\n'
. Beispiel:
$ printf '[%s]\n' f*
[fonts]
[fstab]
[fuse.conf]
[fwupd]
$ printf '[%s]\n' {1,3}{a..c}
[1a]
[1b]
[1c]
[3a]
[3b]
[3c]
Da Sie erst vor kurzem mit Shell-Skripten begonnen haben,Bash-Referenzhandbuchkann eine Herausforderung sein, und Sie möchten es vielleicht nicht von vorne bis hinten lesen. Aber ich denke, es ist eine wertvolle Ressource, selbst wenn Sie sich als absoluter Anfänger betrachten. Vielleicht finden Sie den Abschnitt überShell-Parameternützlich, da es mit dem beginnt, was Sie bereits wissen – Shell-Variablen – und dann zu speziellen Parametern wie ?
(was die Leute oft als $?
Parameter bezeichnen, da Sie es so erweitern). Für allgemeines Lernen über Bash, insbesondere auf einer eher einführenden Ebene, empfehle ichdiese Seiten, einschließlichBashGuide.
Antwort3
Ein gutes Buch, das Sie kennen sollten, ist William Shotts' "The Linux Command Line", erschienen bei No Starch Press und als kostenloses PDF erhältlich auf derWebsite des Autors.
In jedem Shell-Skript gibt es eine Sammlung nummerierter Variablen, die im Allgemeinen als $1
, $2
, usw. bezeichnet werden. Dies sind die „Positionsparameter“, besser bekannt als Befehlszeilenargumente. Sie können sich diese als Variablen mit den Namen 1
, 2
, usw. vorstellen und um ihre Werte zu erhalten, verwenden Sie $1
, $2
, usw. Wenn Sie ein Skript mit dem Namen my_script
über die Befehlszeile aufrufen ./my_script a b c
, erhält es drei Argumente, die in den drei Variablen $1
, $2
, und gespeichert sind $3
. Sie können diesen Variablen keine Zuweisungen geben (außer als Gruppe), aber Sie können ihre Werte untersuchen und verwenden. Beispielsweise echo "$1"
würde das erste Argument in Ihr Skript ausgegeben.
$0
ist etwas ungewöhnlich; es ist der Name, unter dem das von Ihnen ausgeführte Skript aufgerufen wurde. Im obigen Fall hätte es den Wert ./my_script
. Auch hier können Sie den Wert sehen, aber nicht ändern.
$?
ist der „Exit-Status“ des gerade ausgeführten Befehls. Wenn der Befehl erfolgreich war, lautet sein Exit-Status 0
und andernfalls ist es eine kleine positive Ganzzahl. Sie können $?
mit Null vergleichen, um zu sehen, ob der vorherige Befehl erfolgreich war oder fehlgeschlagen ist. Beispielsweise führen die folgenden beiden Befehlszeilen den grep
Befehl aus und geben dann ein Echo aus, <1>
weil der grep
Befehl fehlgeschlagen ist und mit dem Status 1
(was anzeigt, dass er fehlgeschlagen ist) beendet wurde.
grep blarg /etc/passwd
echo "<$?>"
Der Beendigungsstatus ist hilfreich beim Schreiben einfacher Skripte wie:
#!/bin/bash
# grep_pw: grep for a pattern in /etc/passwd
grep "$1" /etc/passwd
if [[ $? = 0 ]] ;then
echo "Found it"
exit 0
else
echo "Unable to find the pattern '$1' in /etc/passwd"
exit 1
fi
Fügen Sie diesen Text in eine Datei mit dem Namen ein grep_pw
, ändern Sie ihn so, dass er mit ausführbar ist chmod 700 grep_pw
, und rufen Sie ihn wie folgt auf, ./grep_pw nologin
um die Zeilen zu finden, /etc/passwd
die die Zeichenfolge enthalten nologin
.
Als ich zum ersten Mal etwas über die Shell hörte, fand ich das folgende Skript von unschätzbarem Wert, um zu verstehen, wie die Shell Befehlszeilen analysiert und welche Befehlszeilenargumente folglich an ein Skript übergeben würden.
#!/bin/bash
# echoargs: print all the arguments
counter=1
for a in "$@" ;do
echo "arg $counter=<$a>"
let counter=counter+1
done
Fügen Sie diesen Inhalt in eine Datei mit dem Namen ein echoargs
, ändern Sie sie so, dass sie mit ausführbar ist, chmod 700 echoargs
und rufen Sie sie wie folgt auf: ./echoargs a "b c" d
.