Ich habe einen komplexen Befehl, aus dem ich ein Shell / Bash-Skript erstellen möchte. Ich kann es in Bezug auf $1
leicht schreiben :
foo $1 args -o $1.ext
Ich möchte in der Lage sein, mehrere Eingabenamen an das Skript zu übergeben. Was ist der richtige Weg, um es zu tun?
Und natürlich möchte ich Dateinamen mit Leerzeichen behandeln.
bash
command-line
Thelema
quelle
quelle
Antworten:
Verwenden Sie
"$@"
diese Option , um alle Argumente darzustellen:Dadurch wird jedes Argument durchlaufen und in einer separaten Zeile ausgedruckt. $ @ verhält sich wie $ *, außer dass die Argumente beim Zitieren richtig aufgeteilt werden, wenn sie Leerzeichen enthalten:
quelle
"$@"
dass es sich zu nichts erweitert, wenn es keine Positionsparameter gibt, während es"$*"
sich zu einer leeren Zeichenfolge erweitert - und ja, es gibt einen Unterschied zwischen keinen Argumenten und einem leeren Argument. Siehe${1:+"$@"} in
/ bin / sh` .$var
Ich konnte die Argumente ausführen.shift
wirft weg$1
und verschiebt alle nachfolgenden Elemente nach unten.Umschreiben einer jetzt gelöschten Antwort von VonC .
Die prägnante Antwort von Robert Gamble befasst sich direkt mit der Frage. Dieser wird bei einigen Problemen mit Dateinamen, die Leerzeichen enthalten, erweitert.
Siehe auch: $ {1: + "$ @"} in / bin / sh
Grundlegende These:
"$@"
ist richtig und$*
(nicht zitiert) ist fast immer falsch. Dies liegt daran, dass es gut"$@"
funktioniert, wenn Argumente Leerzeichen enthalten, und genauso funktioniert, wie$*
wenn dies nicht der Fall ist. Unter bestimmten Umständen"$*"
ist das auch in Ordnung,"$@"
funktioniert aber normalerweise (aber nicht immer) an denselben Orten. Nicht zitiert$@
und$*
gleichwertig (und fast immer falsch).Also, was ist der Unterschied zwischen
$*
,$@
,"$*"
und"$@"
? Sie beziehen sich alle auf 'alle Argumente zur Shell', aber sie machen verschiedene Dinge. Wenn nicht zitiert,$*
und$@
machen Sie das Gleiche. Sie behandeln jedes 'Wort' (Folge von Nicht-Leerzeichen) als separates Argument. Die Anführungszeichen in Anführungszeichen sind jedoch sehr unterschiedlich:"$*"
Behandelt die Argumentliste als einzelne durch Leerzeichen getrennte Zeichenfolge, während"$@"
die Argumente fast genau so behandelt werden, wie sie in der Befehlszeile angegeben wurden."$@"
erweitert sich zu nichts, wenn es keine Positionsargumente gibt;"$*"
erweitert sich zu einer leeren Zeichenfolge - und ja, es gibt einen Unterschied, obwohl es schwierig sein kann, ihn wahrzunehmen. Weitere Informationen finden Sie unten nach der Einführung des Befehls (nicht Standard)al
.Sekundärthese: Wenn Sie Argumente mit Leerzeichen verarbeiten und dann an andere Befehle weitergeben müssen, benötigen Sie manchmal nicht standardmäßige Tools zur Unterstützung. (Oder Sie sollten Arrays vorsichtig verwenden:
"${array[@]}"
verhält sich analog zu"$@"
.)Beispiel:
Warum funktioniert das nicht? Es funktioniert nicht, weil die Shell Anführungszeichen verarbeitet, bevor sie Variablen erweitert. Um die Shell dazu zu bringen, auf die darin eingebetteten Anführungszeichen zu achten
$var
, müssen Sie Folgendes verwendeneval
:Dies wird sehr schwierig, wenn Sie Dateinamen wie "
He said, "Don't do this!"
" (mit Anführungszeichen und doppelten Anführungszeichen und Leerzeichen) haben.Die Shells (alle) machen es nicht besonders einfach, mit solchen Dingen umzugehen, so dass (komischerweise) viele Unix-Programme nicht gut damit umgehen können. Unter Unix kann ein Dateiname (einzelne Komponente) beliebige Zeichen außer Schrägstrich und NUL enthalten
'\0'
. Die Shells empfehlen jedoch nachdrücklich keine Leerzeichen, Zeilenumbrüche oder Tabulatoren in Pfadnamen. Aus diesem Grund enthalten Standard-Unix-Dateinamen keine Leerzeichen usw.Wenn Sie mit Dateinamen arbeiten, die Leerzeichen und andere störende Zeichen enthalten können, müssen Sie äußerst vorsichtig sein, und ich habe vor langer Zeit festgestellt, dass ich ein Programm benötige, das unter Unix nicht Standard ist. Ich nenne es
escape
(Version 1.1 war vom 1989-08-23T16: 01: 45Z datiert).Hier ist ein Beispiel für die
escape
Verwendung - mit dem SCCS-Steuerungssystem. Es ist ein Coverskript, das sowohl eindelta
(Think Check-In ) als auch einget
(Think Check-Out ) ausführt . Insbesondere verschiedene Argumente-y
(der Grund, warum Sie die Änderung vorgenommen haben) würden Leerzeichen und Zeilenumbrüche enthalten. Beachten Sie, dass das Skript aus dem Jahr 1992 stammt und daher Back-Ticks anstelle der$(cmd ...)
Notation verwendet und nicht#!/bin/sh
in der ersten Zeile verwendet wird.(Ich würde Escape heutzutage wahrscheinlich nicht mehr so gründlich verwenden - es wird
-e
zum Beispiel für das Argument nicht benötigt -, aber insgesamt ist dies eines meiner einfacheren Skripteescape
.)Das
escape
Programm gibt einfach seine Argumente aus, ähnlich wie dies derecho
Fall ist, stellt jedoch sicher, dass die Argumente für die Verwendung mit geschützt sindeval
(eine Ebene voneval
; Ich habe ein Programm, das eine Remote-Shell-Ausführung durchgeführt hat und das der Ausgabe von entkommen mussescape
).Ich habe ein anderes Programm namens
al
, das seine Argumente eins pro Zeile auflistet (und es ist noch älter: Version 1.1 vom 1987-01-27T14: 35: 49). Dies ist am nützlichsten beim Debuggen von Skripten, da es in eine Befehlszeile eingefügt werden kann, um zu sehen, welche Argumente tatsächlich an den Befehl übergeben werden.[ Hinzugefügt: Um nun den Unterschied zwischen den verschiedenen
"$@"
Notationen zu zeigen , hier noch ein Beispiel:Beachten Sie, dass durch nichts die ursprünglichen Leerzeichen zwischen
*
und*/*
in der Befehlszeile erhalten bleiben . Beachten Sie außerdem, dass Sie die 'Befehlszeilenargumente' in der Shell ändern können, indem Sie Folgendes verwenden:Dies setzt 4 Optionen, '
-new
', '-opt
', 'and
' und 'arg with space
'.]]
Hmm, das ist eine ziemlich lange Antwort - vielleicht ist Exegese der bessere Begriff. Quellcode für
escape
auf Anfrage erhältlich (E-Mail an Vorname und Nachname bei gmail dot com). Der Quellcode füral
ist unglaublich einfach:Das ist alles. Es entspricht dem
test.sh
Skript, das Robert Gamble gezeigt hat, und kann als Shell-Funktion geschrieben werden (aber Shell-Funktionen waren in der lokalen Version der Bourne-Shell beim ersten Schreiben nicht vorhandenal
).Beachten Sie auch, dass Sie
al
als einfaches Shell-Skript schreiben können :Die Bedingung wird benötigt, damit sie keine Ausgabe erzeugt, wenn keine Argumente übergeben werden. Der
printf
Befehl erzeugt eine leere Zeile nur mit dem Formatzeichenfolgenargument, aber das C-Programm erzeugt nichts.quelle
Beachten Sie, dass Roberts Antwort richtig ist und auch funktioniert
sh
. Sie können es (portabel) noch weiter vereinfachen:ist äquivalent zu:
Dh du brauchst nichts!
Testen (
$
ist Eingabeaufforderung):Ich habe dies zuerst in der Unix-Programmierumgebung von Kernighan und Pike gelesen .
In
bash
,help for
dokumentiert diese:quelle
"$@"
bedeutet, und wenn man weiß, was esfor i
bedeutet, ist es nicht weniger lesbar alsfor i in "$@"
.for i
besser ist, weil es Tastenanschläge spart. Ich habe die Lesbarkeit vonfor i
und verglichenfor i in "$@"
.Für einfache Fälle können Sie auch verwenden
shift
. Die Argumentliste wird wie eine Warteschlange behandelt. Jedesshift
wirft das erste Argument aus und der Index jedes der verbleibenden Argumente wird dekrementiert.quelle
shift
in einer for-Schleife tun , ist dieses Element immer noch Teil des Arrays, während diesshift
wie erwartet funktioniert.echo "$1"
den Abstand im Wert der Argumentzeichenfolge beibehalten sollten . Außerdem (ein kleines Problem) ist dies destruktiv: Sie können die Argumente nicht wiederverwenden, wenn Sie sie alle verschoben haben. Oft ist das kein Problem - Sie verarbeiten die Argumentliste ohnehin nur einmal.--file myfile.txt
$ 1 ist der Parameter, $ 2 ist der Wert und Sie rufen Shift zweimal auf, wenn Sie ein anderes Argument überspringen müssenSie können auch als Array-Elemente auf sie zugreifen, wenn Sie beispielsweise nicht alle Elemente durchlaufen möchten
quelle
./my-script.sh param1 "param2 is a quoted param"
Folgendes verwenden : - Wenn Sie argv = ($ @) verwenden, erhalten Sie [param1, param2]. - Wenn Sie argv = ("$ @") verwenden, erhalten Sie [param1, param2 ist ein zitierter Parameter]quelle
[ $# != 0 ]
ist das besser. Oben#$
sollte sein,$#
wie inwhile [[ $# > 0 ]] ...
Wenn Sie die Argumentliste mit einem Index auflisten müssen (z. B. um nach einem bestimmten Wort zu suchen), können Sie dies tun, ohne die Liste zu kopieren oder zu mutieren.
Angenommen, Sie möchten eine Argumentliste mit einem Doppelstrich ("-") teilen und die Argumente vor den Bindestrichen an einen Befehl und die Argumente nach den Bindestrichen an einen anderen Befehl übergeben:
Die Ergebnisse sollten folgendermaßen aussehen:
quelle
getopt Verwenden Sie den Befehl in Ihren Skripten, um alle Befehlszeilenoptionen oder -parameter zu formatieren.
quelle