Como uso $ * ao omitir determinadas variáveis ​​de entrada como $ 1 e $ 2 no script bash?

15

Por exemplo,

elif [[ $append = $1 ]]
then
  touch ~/directory/"$2".txt
  echo "$variable_in_question" >> ~/directory/"$2".txt

para criar um arquivo de texto contendo todas as entradas a seguir "$2"ou anexar um arquivo de texto existente com todas as entradas a seguir "$2", o que eu usaria no lugar da "$variable_in_question"linha 4?

Eu basicamente quero "$*", mas omitindo "$1"e "$2".

Oreoplasma
fonte

Respostas:

32

Você pode usar a bashexpansão de parâmetro para especificar um intervalo, isso também funciona com parâmetros posicionais. Para $3... $nseria:

"${@:3}" # expands to "$3" "$4" "$5" …
"${*:3}" # expands to "$3 $4 $5 …"

Esteja ciente de que ambos $@e $*ignore o primeiro argumento $0. Se você se pergunta qual deles usar no seu caso: é muito provável que você queira um citado $@. Não use a $*menos que você não queira explicitamente que os argumentos sejam citados individualmente.

Você pode experimentá-lo da seguinte maneira:

$ bash -c 'echo "${@:3}"' 0 1 2 3 4 5 6
3 4 5 6
$ echo 'echo "${@:3}"' >script_file
$ bash script_file 0 1 2 3 4 5 6
2 3 4 5 6

Observe que no primeiro exemplo $0é preenchido o primeiro argumento, 0enquanto quando usado em um script $0é preenchido com o nome do script, como mostra o segundo exemplo. O nome do script, ébash claro, é o primeiro argumento, apenas que normalmente não é percebido como tal - o mesmo vale para um script tornado executável e chamado "diretamente". Portanto, no primeiro exemplo, temos $0= 0, $1= 1etc, enquanto no segundo é $0= script_file, $1= 0, $2= 1etc .; ${@:3}seleciona todos os argumentos que começam com $3.

Alguns exemplos adicionais para possíveis intervalos:

 # two arguments starting with the third
$ bash -c 'echo "${@:3:2}"' 0 1 2 3 4 5 6
3 4
 # every argument starting with the second to last one
 # a negative value needs either a preceding space or parentheses
$ bash -c 'echo "${@: -2}"' 0 1 2 3 4 5 6
5 6
 # two arguments starting with the fifth to last one
$ bash -c 'echo "${@:(-5):2}"' 0 1 2 3 4 5 6
2 3

Leitura adicional:

sobremesa
fonte
27

Você pode usar o shiftbuiltin:

$ help shift
shift: shift [n]
    Shift positional parameters.

    Rename the positional parameters $N+1,$N+2 ... to $1,$2 ...  If N is
    not given, it is assumed to be 1.

    Exit Status:
    Returns success unless N is negative or greater than $#.

Ex. dado

$ cat argtest.bash 
#!/bin/bash

shift 2

echo "$*"

então

$ ./argtest.bash foo bar baz bam boo
baz bam boo
chave de aço
fonte
3
@ Oreoplasm Acho que vale a pena mencionar que a shiftabordagem tornará impossível o acesso $1e $2depois que você as mudou. No seu script que você usa $2ao lado de $variable_in_question, você precisa mudar isso ou usar a abordagem de expansão de parâmetros.
dessert
6
shifté bom quando o primeiro ou mais argumentos são especiais e faz sentido para pegá-los para fora em variáveis separadas ( foo="$1"; bar="$2";ou if [[ something with $1 ]];then blah blah; shift. Mas @ resposta de sobremesa com a abordagem não-destrutiva é bom em outros casos quando você não ainda quero a lista completa mais tarde ou quando você usar a sintaxe mais extravagante para escolher uma gama limitada de args, não ao infinito, sem introduzir argumentos vazios para um comando se $@não tem que muitos elementos.
Peter Cordes
13

Geralmente, você pode copiar os parâmetros posicionais em uma matriz, excluir índices arbitrários da matriz e, em seguida, usar a matriz para expandir exatamente para os índices desejados, sem perder os argumentos originais.

Por exemplo, se eu quisesse todos os argumentos, exceto o primeiro, o quarto e o quinto:

args=( "$@" )
unset args[0] args[3] args[4]
echo "${args[@]}"

Na cópia, os índices são alterados por 1, pois $0não fazem parte de $@.

muru
fonte
11
É claro que é possível combinar isso, por exemplo, echo "${args[@]:2}"do terceiro ao último argumento.
dessert