$ @ exceto o primeiro argumento

23

Eu preciso escrever um script de shell que seja executado desta maneira:

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

existe um loop for dentro do script

for i in $@

No entanto, como eu sei, $ @ inclui $ 1 até $ ($ # - 1). Mas para o meu programa $ 1 é distintamente diferente de $ 2 $ 3 $ 4 etc. Eu gostaria de fazer um loop de $ 2 até o final ... Como faço para conseguir isso? Obrigado :)

    
por user40780 27.08.2015 / 21:45

4 respostas

31

Primeiro, observe que $@ sem aspas não faz sentido e não deve ser usado. $@ só deve ser usado entre aspas ( "$@" ) e em contextos de lista.

for i in "$@" se qualifica como um contexto de lista, mas aqui, para fazer um loop sobre os parâmetros posicionais, a forma canônica, mais portátil e mais simples é:

for i
do something with "$i"
done

Agora, para percorrer os elementos a partir do segundo, a maneira mais canônica e mais portátil é usar shift :

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

Depois de shift , o que costumava ser $1 foi removido da lista (mas o salvamos em $first_arg ) e o que costumava estar em $2 agora está em $1 . Os parâmetros posicionais foram alterados 1 position para a esquerda (use shift 2 para mudar em 2 ...). Então, basicamente, nosso loop está em loop do que costumava ser o segundo argumento para o último.

Com bash (e zsh e ksh93 , mas é isso), uma alternativa é fazer:

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

Mas observe que não é a sintaxe sh padrão, portanto, não deve ser usada em um script que comece com #! /bin/sh - .

Em zsh ou yash , você também pode fazer:

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

para fazer um loop do terceiro ao terceiro último argumento.

Em zsh , $@ também é conhecido como o array $argv . Então, para pop elementos a partir do começo ou fim dos arrays, você também pode fazer:

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

( shift também pode ser escrito 1=() in zsh )

Em bash , você só pode atribuir aos elementos $@ com o set incorporado, então, para inserir três elementos no final, seria algo como:

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

E para fazer o loop do terceiro ao terceiro último:

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

POSIXly, para inserir os últimos 3 elementos de "$@" , você precisaria usar um loop:

n=$(($# - 3))
for arg do
  [ "$n" -gt 0 ] && set -- "$@" "$arg"
  shift
  n=$((n - 1))
done
    
por 27.08.2015 / 21:59
9

Eu acho que você quer o shift construído. Ele renomeia $2 para $1 , $3 para $2 , etc.

Assim:

shift
for i in "$@"; do
    echo $i
done
    
por 27.08.2015 / 21:50
2

Sempre há a abordagem do homem das cavernas:

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

Isso deixa $@ intacto (no caso de você querer usá-lo mais tarde), e simplesmente faz um loop em cada argumento, mas não processa o primeiro.

    
por 28.08.2015 / 03:04
1

No bash, você também pode escrever esse loop com indexação explícita:

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

Isso itera sobre todos os argumentos do segundo para o último. Se você quiser excluir o último argumento, simplesmente faça isso

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

e se você quiser apenas usar todos os outros argumentos, escreva-o como

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

A história por trás disso é a aritmética versão do o for builtin , combinado com o argumento-contagem $# e o indireção indireta ${…} .

Um aplicativo interessante é que você pode usar isso para decidir, dentro do loop, se uma determinada opção levará o argumento que o segue como um valor. Se isso acontecer, incremente i (por exemplo, escrevendo : $((++i)) ) para consumir o próximo valor e ignorá-lo durante a iteração.

    
por 28.08.2015 / 14:07