Wie wird das letzte Positionsargument einer Bash-Funktion oder eines Skripts eingefügt?

Sep 28 2020

Ich muss oft das letzte Positionsargument einer Bash-Funktion oder eines Skripts einfügen.

Mit "pop" meine ich: "entferne es aus der Liste der Positionsargumente und ordne es (optional) einer Variablen zu."

Angesichts der Häufigkeit, mit der ich diese Operation benötige, bin ich etwas überrascht, dass das folgende Beispiel das Beste ist, was ich gefunden habe:

foo () {
    local argv=( "$@" ) local last=${argv[$(( ${#argv[@]} - 1 ))]}
    argv=( ${argv[@]:0:$(( ${#argv[@]} - 1 ))} ) echo "last: $last"
    echo "rest: ${argv[@]}"
}

Mit anderen Worten, eine epische Produktion mit einer Besetzung von Tausenden ...

Gibt es etwas Einfacheres, leichter zu lesen?

Antworten

26 Quasímodo Sep 28 2020 at 03:38

Sie können mit ${argv[-1]}(bash 4.2 oder höher) auf das letzte Element zugreifen und es mit dem unseteingebauten Element (bash 4.3 oder höher) aus dem Array entfernen :

last=${argv[-1]}
unset 'argv[-1]'

Die Anführungszeichen argv[-1]sind erforderlich, ebenso [...]wie ein Glob-Operator, sodass argv[-1]nicht zitierte Dateien auf argv-und / oder erweitert werden können, argv1wenn diese Dateien im aktuellen Verzeichnis vorhanden sind (oder auf nichts oder einen Fehler verursachen, wenn sie nicht mit nullglob/ failglobaktiviert sind).

15 GordonDavisson Sep 28 2020 at 06:03

Wie wäre es damit:

foo () {
    local last="${!#}" local argv=("${@:1:$#-1}") echo "last: $last"
    echo "rest: ${argv[@]}"
}
6 bxm Sep 28 2020 at 03:38

Sie können ein wenig vereinfachen, um das Auge zu entlasten , aber die grundlegende Methode bleibt unverändert (dies kann nützlich sein, wenn Sie eine Bash-Version haben, die das Angebot von Quasímodo nicht unterstützt ):

foo () {
    local argv=( "$@" ) local last="${argv[$# - 1]}" argv=( "${argv[@]:0:$# - 1}" ) echo "last: $last"
    echo "rest: ${argv[@]}"
}

Ich gebe zu, dass es $#auf diese Weise eine etwas freche Verwendung ist , aber es hat den gleichen Wert wie ${#argv[@]} in diesem speziellen Beispiel und ist im Code prägnanter.

3 StéphaneChazelas Sep 29 2020 at 00:50

Für die Aufzeichnung im Falle switching, um zsheine Option, in zshdas Array von Positionsparameter ist die $argvAnordnung (zsh Arrays sind echte Arrays und mit Indizes auf eine wie die Positionsparameter ausgehend), so gibt, ist es nur:

foo () {
    local last=$argv[-1]
    argv[-1]=()
    print -rC1 "last: $last" rest: ' - '$^argv
}

Sie können aber auch Folgendes tun:

foo () {
    print -rC1 "last: $argv[-1]" rest: ' - '$^argv[1,-2]
}

Und wenn das Endziel darin besteht, die Positionsparameter in umgekehrter Reihenfolge zu durchlaufen:

for arg in "${(Oa)@}"; do something with "$arg"
done
wchargin Sep 30 2020 at 14:24

Für eine einfachere entspricht bxm Antwort , Verwendung set:

pop() {
  last="${!#}" # see man bash, search for "indirect expansion" set -- "${@:1:$#-1}" printf 'not last: %s\n' "$@"
  printf 'last: %s\n' "${last}"
}

pop 1 2 3 4
# prints:
# not last: 1
# not last: 2
# not last: 3
# last: 4

Hier ${!#}handelt es sich um eine indirekte Parametererweiterung (Suche nach "indirekte Erweiterung" in man bash), und set -- ARGS...die neuen Werte der Argumente ( $1usw.) werden auf die angegebenen Argumente gesetzt .

Insbesondere ${!#}und das Array-Slicing sind Bashismen, können aber set --auf jede POSIX-Shell portiert werden .