$ @ excepto el primer argumento

36

Necesito escribir un script de shell que se ejecute de esta manera:

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

hay un bucle for dentro del script

for i in $@

Sin embargo, como sé, $ @ incluye $ 1 hasta $ ($ # - 1). Pero para mi programa, $ 1 es claramente diferente de $ 2 $ 3 $ 4, etc. Me gustaría pasar de $ 2 al final ... ¿Cómo puedo lograr esto? Gracias:)

usuario40780
fuente

Respuestas:

47

Primero, tenga en cuenta que $@sin comillas no tiene sentido y no debe usarse. $@solo debe usarse entre comillas ( "$@") y en contextos de lista.

for i in "$@" califica como un contexto de lista, pero aquí, para recorrer los parámetros posicionales, la forma canónica, más portátil y más simple es:

for i
do something with "$i"
done

Ahora, para recorrer los elementos a partir del segundo, la forma canónica y más portátil es usar shift:

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

Después shift, lo que solía ser $1se ha eliminado de la lista (pero lo hemos guardado $first_arg) y lo que solía estar $2ahora está dentro $1. Los parámetros posicionales se han desplazado 1 hacia la izquierda (se usa shift 2para cambiar por 2 ...). Básicamente, nuestro bucle se repite desde lo que solía ser el segundo argumento hasta el último.

Con bash(y zshy ksh93, pero eso es todo), una alternativa es hacer:

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

Pero tenga en cuenta que no es una shsintaxis estándar, por lo que no debe usarse en un script que comience con #! /bin/sh -.

En zsho yash, también puedes hacer:

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

pasar del 3er al 3er último argumento.

En zsh, $@también se conoce como la $argvmatriz. Entonces, para resaltar elementos desde el principio o el final de las matrices, también puede hacer:

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

( shifttambién se puede escribir 1=()en zsh)

En bash, solo puede asignar a los $@elementos con la función setincorporada, por lo que sacar 3 elementos del final, sería algo así como:

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

Y para pasar del 3 al 3 último:

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

POSIXY, para hacer estallar los últimos 3 elementos de "$@", necesitaría usar un bucle:

n=$(($# - 3))
for arg do
  [ "$n" -gt 0 ] && set -- "$@" "$arg"
  shift
  n=$((n - 1))
done
Stéphane Chazelas
fuente
2
Una posibilidad de golpe alternativo (y feo): variables indirectas:for ((i=2; i<=$#; i++)); do something with "${!i}"; done
Glenn Jackman
Estoy más familiarizado con esta versión, ya que estoy más familiarizado con c ++ :)
user40780
10

Creo que quieres el shiftincorporado. Renombra $2a $1, $3a $2, etc.

Me gusta esto:

shift
for i in "$@"; do
    echo $i
done
John
fuente
¿Podría explicar con más detalle cómo puedo lograr eso en el ciclo for? Gracias.
user40780
1
No lo hace, lo usa antes de ingresar al forciclo, luego simplemente recorre $ @ normalmente. Después de la shiftllamada, $ @ debería serarg2_1 arg2_2 arg2_3...
John
Sin embargo, tendré una pregunta más: supongamos que quiero pasar de $ 1 a $ ($ # - 2) (es decir, arg_1 hasta arg_2 _ # - 1, excepto arg_2 _ #) ... ¿Qué debo hacer?
user40780
2

Siempre existe el enfoque del hombre de las cavernas:

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

Esto deja $@intacto (en caso de que quiera usarlo más tarde), y simplemente recorre cada argumento, pero no procesa el primero.

Scott
fuente
1

En bash también puedes escribir ese ciclo con indexación explícita:

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

Esto itera sobre todos los argumentos desde el segundo hasta el último. Si desea excluir el último argumento, simplemente haga esto

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

y si solo quieres tomar cualquier otro argumento, escríbelo como

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

La historia detrás de esto es la versión aritmética de la forconstrucción , combinada con el conteo de argumentos$# y la indirección variable${…} .

Una buena aplicación es que puede usar esto para decidir, dentro del ciclo, si una opción dada tomará el argumento que le sigue como un valor. Si lo hace, incremente i(por ejemplo, escribir : $((++i))) para consumir el siguiente valor y omítalo durante la iteración.

MvG
fuente