Was ist ein besserer Weg, um zu implementieren print_last_arg
?
#!/bin/sh
print_last_arg () {
eval "echo \${$#}" # this hurts
}
print_last_arg foo bar baz
# baz
(Wenn dies beispielsweise wäre, #!/usr/bin/zsh
anstatt zu #!/bin/sh
wissen, was zu tun ist. Mein Problem besteht darin, einen vernünftigen Weg zu finden, dies zu implementieren #!/bin/sh
.)
EDIT: Das obige ist nur ein dummes Beispiel. Mein Ziel ist es nicht, das letzte Argument zu drucken , sondern eine Möglichkeit zu haben, auf das letzte Argument innerhalb einer Shell-Funktion zu verweisen.
EDIT2: Ich entschuldige mich für eine so unklar formulierte Frage. Ich hoffe, es diesmal richtig zu machen.
Wenn dies /bin/zsh
stattdessen wäre /bin/sh
, könnte ich so etwas schreiben
#!/bin/zsh
print_last_arg () {
local last_arg=$argv[$#]
echo $last_arg
}
Der Ausdruck $argv[$#]
ist ein Beispiel für das, was ich in meiner ersten EDIT beschrieben habe , um auf das letzte Argument innerhalb einer Shell-Funktion zu verweisen .
Deshalb hätte ich mein ursprüngliches Beispiel wirklich so schreiben sollen:
print_last_arg () {
local last_arg=$(eval "echo \${$#}") # but this hurts even more
echo $last_arg
}
... um klar zu machen, dass das, wonach ich suche, eine weniger schreckliche Sache ist, die ich rechts von der Aufgabe setzen muss.
Beachten Sie jedoch, dass in allen Beispielen zerstörungsfrei auf das letzte Argument zugegriffen wird . IOW, der Zugriff auf das letzte Argument lässt die Positionsargumente insgesamt unberührt.
quelle
var=$( eval echo \${$#})
zueval var=\${$#}
- die beiden nichts gleicht dem anderen .shift
undset -- ...
basierte Lösungen können destruktiv sein, wenn sie nicht in Funktionen verwendet werden, in denen sie auch harmlos sind.eval "var=\${$#}"
alsvar=${arr[evaled index]}
dass dies$#
ein garantierter sicherer Wert ist. Warum das ganze Set kopieren und dann zerstören, wenn Sie es einfach direkt indizieren können?Antworten:
... gibt entweder die Zeichenfolge
the last arg is
gefolgt von einem <Leerzeichen> , dem Wert des letzten Arguments und einem nachfolgenden <Newline> aus, wenn mindestens 1 Argument vorhanden ist, oder gibt bei Nullargumenten überhaupt nichts aus.Wenn du. .. getan hast:
... dann würden Sie entweder der Shell-Variablen den Wert des letzten Arguments zuweisen,
$lastarg
wenn mindestens 1 Argument vorhanden ist, oder Sie würden überhaupt nichts tun. In jedem Fall würden Sie es sicher tun, und es sollte sogar für die Olde Bourne-Shell tragbar sein, denke ich.Hier ist eine andere, die ähnlich funktionieren würde, obwohl das gesamte arg-Array zweimal kopiert werden muss (und ein
printf
In$PATH
für die Bourne-Shell erforderlich ist ) :quelle
${1+":"} return
sofort öffnen - denn wer möchte, dass sie etwas tut oder Nebenwirkungen jeglicher Art riskiert, wenn sie umsonst aufgerufen wird? Mathe ist genauso schön - wenn Sie sicher sein können, dass Sie eine positive ganze Zahl erweitern können, können Sie das deneval
ganzen Tag.$OPTIND
ist toll dafür.Hier ist ein vereinfachter Weg:
(aktualisiert basierend auf dem Punkt von @ cuonglm, dass das Original fehlgeschlagen ist, wenn keine Argumente übergeben wurden; dies gibt jetzt eine leere Zeile wieder - ändern Sie dieses Verhalten in der
else
Klausel, falls gewünscht)quelle
echo "$# - 1" | bc
Am Beispiel des Eröffnungspostens (Positionsargumente ohne Leerzeichen):
Für den Standard
IFS=' \t\n'
, wie etwa:Für eine sicherere Erweiterung von
"$*"
setzen Sie IFS (per @ StéphaneChazelas):Das Obige schlägt jedoch fehl, wenn Ihre Positionsargumente Leerzeichen enthalten können. Verwenden Sie in diesem Fall stattdessen Folgendes:
Beachten Sie, dass diese Techniken die Verwendung vermeiden
eval
und keine Nebenwirkungen haben.Getestet bei shellcheck.net
quelle
$IFS
ein Leerzeichen ist.IFS=' '
hier zu sein, nur um klar zu machen, dass es für die Erweiterung von verwendet wird"$*"
.POSIXly:
(Dieser Ansatz funktioniert auch in der alten Bourne-Shell)
Mit anderen Standardwerkzeugen:
(Dies funktioniert nicht mit alten
awk
, die nicht hattenARGV
)quelle
awk
könnte auch die alte Awk sein, die es nicht gabARGV
.awk
Ansatz oder eine andere einfachefor
Schleife wie andere oder übergeben Sie sie an eine Funktion.Dies sollte mit jeder POSIX-kompatiblen Shell funktionieren und auch mit der älteren Solaris Bourne-Shell vor POSIX:
und hier ist eine Funktion, die auf dem gleichen Ansatz basiert:
PS: Sag mir nicht, dass ich die geschweiften Klammern um den Funktionskörper vergessen habe ;-)
quelle
in ...
in einerfor
Schleife als auch eine geschweifte Klammer um eineif
Anweisung, die als Funktionskörper verwendet wird. Siehefor_clause
undcompound_command
in pubs.opengroup.org/onlinepubs/9699919799 .Aus "Unix - Häufig gestellte Fragen"
(1)
Wenn die Anzahl der Argumente Null sein könnte, wird dem Argument Null
$0
(normalerweise der Name des Skripts) zugewiesen$last
. Das ist der Grund für das Wenn.(2)
(3)
Um zu vermeiden, dass eine leere Zeile gedruckt wird, wenn keine Argumente vorhanden sind, ersetzen Sie das
echo "$last"
for:Eine Anzahl von Nullargumenten wird durch vermieden
if [ $# -gt 0 ]
.Dies ist keine exakte Kopie dessen, was auf der Seite verlinkt ist. Einige Verbesserungen wurden hinzugefügt.
quelle
Obwohl diese Frage etwas mehr als 2 Jahre alt ist, dachte ich, ich würde eine etwas kompakte Option teilen.
Lass es uns laufen
Erweiterung der Bash- Shell-Parameter .
Bearbeiten
Noch prägnanter:
echo "${@: -1}"
(Achten Sie auf den Raum)
Quelle
Getestet unter macOS 10.12.6, sollte aber auch das letzte Argument für die meisten verfügbaren * nix-Varianten zurückgeben ...
Tut viel weniger weh
¯\_(ツ)_/¯
quelle
echo "${*: -1}"
worüber manshellcheck
sich nicht beschweren wird.${array:n:m:}
ist eine Erweiterung. (Die Frage wurde ausdrücklich erwähnt/bin/sh
)Dies sollte auf allen
perl
installierten Systemen funktionieren (daher die meisten UNICES):Der Trick besteht darin
printf
,\0
nach jedem Shell-Argument und dannperl
den-0
Schalter einzufügen, der das Datensatztrennzeichen auf NULL setzt. Dann iterieren wir über die Eingabe, entfernen die\0
und speichern jede NULL-terminierte Zeile als$l
. DerEND
Block (das ist, was der}{
ist) wird ausgeführt, nachdem alle Eingaben gelesen wurden, und gibt die letzte "Zeile" aus: das letzte Shell-Argument.quelle
sed
,awk
oderperl
dann könnte es trotzdem wertvolle Informationen sein. Trotzdem können Sie dies auchsed -z
für aktuelle GNU-Versionen tun .Hier ist eine Version mit Rekursion. Keine Ahnung, wie POSIX-konform dies ist ...
quelle
Ein einfaches Konzept, keine Arithmetik, keine Schleife, keine Auswertung , nur Funktionen.
Denken Sie daran, dass die Bourne-Shell keine Arithmetik hatte (extern benötigt
expr
). Wenn Sie eine arithmetikfreieeval
Wahl treffen möchten , ist dies eine Option. Benötigte Funktionen bedeutet SVR3 oder höher (kein Überschreiben von Parametern).Unten finden Sie eine robustere Version mit printf.
Diese Struktur Anruf
printlast
wird fixiert , die Argumente in der Liste der Argumente Schale festgelegt werden müssen$1
,$2
usw. (das Argument stack) und der Anruf wird als gegeben getan.Wenn die Liste der Argumente geändert werden muss, legen Sie sie einfach fest:
Oder erstellen Sie eine benutzerfreundlichere Funktion (
getlast
), die generische Argumente zulässt (aber nicht so schnell, Argumente werden zweimal übergeben).Bitte beachten Sie, dass Argumente (von
getlast
oder alle in$@
printlast enthaltenen) Leerzeichen, Zeilenumbrüche usw. enthalten können, jedoch nicht NUL.Besser
Diese Version wird nicht gedruckt,
0
wenn die Liste der Argumente leer ist, und verwendet das robustere printf (greifen Sie auf zurück,echo
wennprintf
für alte Shells kein externes verfügbar ist).EVAL verwenden.
Wenn die Bourne-Shell noch älter ist und keine Funktionen vorhanden sind oder wenn aus irgendeinem Grund die Verwendung von eval die einzige Option ist:
So drucken Sie den Wert:
So legen Sie den Wert in einer Variablen fest:
Wenn dies in einer Funktion erfolgen muss, müssen die Argumente in die Funktion kopiert werden:
quelle
eval
?for loop
oderwhile loop
?echo "Hello"
hat Schleifen, da die CPU Speicherstellen in einer Schleife lesen muss. Nochmals: Mein Code hat keine zusätzlichen Schleifen.for
,while
oderuntil
Schleifen. Sehen Sie einefor
while
oder eineuntil
Schleife im Code?. Nein?, Dann akzeptiere einfach die Tatsache, nimm sie an und lerne.Dieser Kommentar schlug vor, das sehr elegante zu verwenden:
quelle