Ich habe ein Skript, das N Parameter abruft. Es analysiert sie zuerst (extrahiert einen bestimmten Wert X
) und ruft dann ein anderes Programm mit diesem Wert auf:
function main() {
parse "$@"
run "$@"
}
main "$@"
Ich möchte am Anfang der Argumentliste einen optionalen Parameter hinzufügen (Parameter zum Festlegen der von mir ausgeführten Python-Version). Daher habe ich Folgendes zur Analysefunktion hinzugefügt:
if [ "$1" = '2' ] || [ "$1" = '3' ]; then version=$1 && shift; else version=2; fi
Nachdem die Analyse beendet ist, stecke ich jedoch fest, da dies shift
die Ausführungsfunktion nicht beeinflusst. Wie kann ich dies tun, ohne den Wert des ersten Werts erneut zu überprüfen (was den gesamten Zweck einer Analysefunktion zunichte machen würde)?
Antwort1
Der Umfang jeder Shell-Funktion (sowie der Umfang des Top-Level-Codes im Skript) hat seine eigenePositionsparameterund kann nur direkt auf sich selbst zugreifen. Die sauberste Lösung besteht darin, die Werte der Positionsparameter, an denen Sie interessiert sind, ineinAnordnung. Sie können dieses Array dann in mehreren Funktionsbereichen lesen und auch ändern.
Beispielsweise könnte dieser Code am Anfang Ihres Skripts erscheinen:
declare -a args=("$@")
- Geben Sie dem Array einen beliebigen Namen. Es muss nicht heißen
args
. - Sie können es sogar weglassen
declare -a
, wenn Sie möchten. - Es muss nicht unbedingt am Anfang erscheinen. Es muss nurlaufenbevor der Code ausgeführt wird, der darauf zugreift
args
. Es könnte sogar unter den Definitionen von Shell-Funktionen erscheinen, die verwendenargs
. Der Übersichtlichkeit halber schlage ich vor, es irgendwo am Anfang zu platzieren.
Dann können Sie das args
Array von mehreren Funktionen aus bearbeiten. Sie müssen es nicht an die Funktionen übergeben; diese können bereits darauf zugreifen. Wenn Code in einer Shell-Funktion den Inhalt ändert args
und dann zurückkehrt, kann Code im Aufrufer die Änderungen beobachten.
Positionsparameter in Shells im Bourne-Stil, einschließlich Bash, verwenden eine 1-basierte Indizierung. (Das liegt daran , dass $0
, das zum Programmnamen erweitert wird, technisch gesehen kein Positionsparameter ist und sich in Funktionsbereichen nicht ändert.) Arrays in Bash verwenden jedoch eine 0-basierte Indizierung. Nach stimmt also args=("$@")
, mit , mit , mit , mit usw. überein. stimmt immer noch wie erwartet überein.$1
${args[0]}
$2
${args[1]}
$3
${args[2]}
$@
${args[@]}
Ich habe sie der Lesbarkeit halber so ohne Anführungszeichen geschrieben. Natürlich werden Sie Erweiterungen, die Ihr args
Array betreffen, fast immer in doppelte Anführungszeichen setzen wollen, so wie Sie Erweiterungen, die Positionsparameter betreffen, fast immer in doppelte Anführungszeichen setzen wollen.
Wenn Sie sich für diesen Ansatz entscheiden, dann anstelle von:
shift
Sie würden schreiben:
args=("${args[@]:1}")
Wenn Sie mit Arrays in Bash noch nicht vertraut sind, sollten Sie einen Blick aufder entsprechende Teil des Bash-Referenzhandbuchs. Vielleicht möchten Sie auch interaktiv experimentieren. Zum Beispiel:
ek@Cord:~$ args=('foo bar' 'baz quux' 'ham spam')
ek@Cord:~$ printf '[%s]\n' "${args[@]}"
[foo bar]
[baz quux]
[ham spam]
ek@Cord:~$ printf '[%s]\n' "${args[@]:1}"
[baz quux]
[ham spam]
Der Code entspricht
if [ "$1" = 2 ] || [ "$1" = 3 ]; then
version="$1"
shift
else
version=2
fi
wäre:
if [ "${args[0]}" = 2 ] || [ "${args[0]}" = 3 ]; then
version="${args[0]}"
args=("${args[@]:1}")
else
version=2
fi
Die Verwendung eines Arrays ist syntaktisch umständlicher, aber auch flexibler.
Andererseits, da Sie den größten Teil des Codes Ihres Skripts run
ohnehin in einer Funktion und ihren aufgerufenen Elementen organisiert zu haben scheinen, könnten Sie die Alternative in Betracht ziehen, alle speziellen Befehlszeilenargumente zu analysieren.VorRufen Sie run
außerhalb einer Shell-Funktion auf und rufen Sie dann run "$@"
wie bisher auf.
Es gibt Programmiersprachen, deren Kulturen eine starke Ethik haben, fast alles in kleine (oder so kleine), in sich geschlossene Funktionen zu packen. Bash ist keine solche Sprache, und die begrenzten Möglichkeiten, komplexe Daten aus einer Shell-Funktion zurückzugeben, sind einer der Gründe. Sie sollten keine Angst davor haben, Shell-Funktionen zu schreiben, und Sie sollten sogar bereit sein, eine große Anzahl kleiner Shell-Funktionen zu schreiben. Aber ich denke, Sie sollten sich keine Sorgen machen, wenn sich herausstellt, dass die beste Form für Ihr Skript eine andere ist.
Weitere Informationen und einige Alternativen, einschließlich eines seltsamen Ansatzes, bei dem Sie die Ausgabe der Prozesssubstitution als Quelle verwenden, finden Sie unterGilles' AntwortZuPositionsparameter des Funktionsaufrufers.