$ @ außer dem 1. Argument

36

Ich muss ein Shell-Skript schreiben, das folgendermaßen ausgeführt wird:

./myscript arg1 arg2_1 arg2_2 arg2_3 ....... arg2_#

Es gibt eine for-Schleife im Skript

for i in $@

Wie ich jedoch weiß, enthält $ @ $ 1 bis $ ($ # - 1). Aber für mein Programm unterscheidet sich $ 1 deutlich von $ 2 $ 3 $ 4 usw. Ich würde gerne eine Schleife von $ 2 bis zum Ende machen ... Wie erreiche ich das? Vielen Dank:)

user40780
quelle

Antworten:

47

Beachten Sie zunächst, dass $@Anführungszeichen keinen Sinn ergeben und nicht verwendet werden sollten. $@sollte nur in Anführungszeichen ( "$@") und in Listenkontexten verwendet werden.

for i in "$@" qualifiziert sich als Listenkontext, aber hier, um die Positionsparameter zu durchlaufen, ist die kanonische, portabelste und einfachste Form:

for i
do something with "$i"
done

Um die Elemente ab dem zweiten zu durchlaufen, ist die kanonischste und portabelste Methode shift:

first_arg=$1
shift # short for shift 1
for i
do something with "$i"
done

Danach shiftwurde das, was früher $1war, aus der Liste entfernt (aber wir haben es gespeichert $first_arg) und das, was früher war, $2ist jetzt in $1. Die Positionsparameter wurden verschoben 1 nach links (verwenden Sieshift 2 mit um 2 verschieben ...). Im Grunde ist unsere Schleife eine Schleife von dem, was früher das zweite bis zum letzten Argument war.

Mit bash(und zshundksh93 , aber das war's) ist eine Alternative:

for i in "${@:2}"
do something with "$i"
done

Beachten Sie jedoch, dass dies kein Standard ist sh daher nicht in einem Skript verwendet werden sollte, das mit beginnt #! /bin/sh -.

In zshoder yashkönnen Sie auch Folgendes tun:

for i in "${@[3,-3]}"
do something with "$i"
done

um vom 3. zum 3. letzten Argument zu gelangen.

In zsh, $@wird auch als $argvArray bezeichnet. Um also Elemente vom Anfang oder Ende der Arrays zu platzieren, können Sie auch Folgendes tun:

argv[1,3]=() # remove the first 3 elements
argv[-3,-1]=()

( shiftKann auch geschrieben werden 1=()inzsh )

In bashkönnen Sie nur den $@Elementen mit dem seteingebauten Code zuweisen. Wenn Sie also 3 Elemente am Ende platzieren, ist das ungefähr so:

set -- "${@:1:$#-3}"

Und um vom 3. zum 3. letzten zu gelangen:

for i in "${@:3:$#-5}"
do something with "$i"
done

POSIX: Um die letzten 3 Elemente von zu platzieren "$@", müssten Sie eine Schleife verwenden:

n=$(($# - 3))
for arg do
  [ "$n" -gt 0 ] && set -- "$@" "$arg"
  shift
  n=$((n - 1))
done
Stéphane Chazelas
quelle
2
Eine alternative (und hässliche) Bash-Möglichkeit: indirekte Variablen:for ((i=2; i<=$#; i++)); do something with "${!i}"; done
Glenn Jackman
Ich bin mit dieser Version besser vertraut, da ich mit c ++ besser vertraut bin :)
user40780
10

Ich denke du willst das shifteingebaute. Es wird $2in $1, $3in $2usw. umbenannt.

So was:

shift
for i in "$@"; do
    echo $i
done
John
quelle
Könntest du genauer erklären, wie ich das in der for-Schleife erreiche? Vielen Dank.
user40780
1
Sie tun es nicht - Sie benutzen es, bevor Sie in die forSchleife eintreten , dann durchlaufen Sie $ @ normal. Nach dem shiftAnruf sollte $ @ seinarg2_1 arg2_2 arg2_3...
John
Ich werde jedoch noch eine Frage haben: Angenommen, ich möchte eine Schleife von $ 1 bis $ ($ # - 2) ausführen (dh arg_1 bis arg_2 _ # - 1, außer arg_2 _ #) ... Was soll ich tun?
user40780
2

Es gibt immer den Höhlenmenschen-Ansatz:

first=1
for i
do
        if [ "$first" ]
        then
                first=
                continue
        fi
        something with "$i"
done

Dies lässt $@intakt (falls Sie es später verwenden möchten) und durchläuft einfach jedes Argument, verarbeitet aber nicht das erste.

Scott
quelle
1

In bash können Sie diese Schleife auch mit expliziter Indizierung schreiben:

for ((i=2; i<=$#; ++i)); do
  process "${!i}"
done

Dies durchläuft alle Argumente vom zweiten bis zum letzten. Wenn Sie stattdessen das letzte Argument ausschließen möchten, geben Sie dies einfach ein

for ((i=1; i<=$#-1; ++i)); do
  process "${!i}"
done

und wenn Sie nur jedes andere Argument nehmen möchten, schreiben Sie es als

for ((i=1; i<=$#; i+=2)); do
  process "${!i}"
done

Die Geschichte dahinter ist die arithmetische Version des forBuilt-Ins , kombiniert mit der Argumentanzahl$# und der variablen Indirektion${…} .

Eine nette Anwendung ist, dass Sie damit innerhalb der Schleife entscheiden können, ob eine gegebene Option das darauf folgende Argument als Wert verwendet. Wenn dies der Fall ist, erhöhen Sie den Wert i(z. B. Schreiben : $((++i))), um den nächsten Wert zu verbrauchen, und überspringen Sie ihn während der Iteration.

MvG
quelle