bash equivalente do $ @ [2, $ #] do zsh

5

(NB: a motivação para esta questão é apenas melhorar o meu know-how de programação bash ).

Eu quero saber o bash equivalente de zsh expressões como $@[2,$#] , que diretamente endereçam um intervalo da matriz de argumentos de linha de comando de uma função shell (ou script) ( $@ ).

(Todas as soluções que encontrei on-line para endereçar intervalos de $@ em bash podem ser descritas como " indiretas endereçamento", pois todas elas exigem primeiro atribuir $@ a um intermediário Veja o exemplo abaixo.)

Por uma questão de concretude: qual seria o bash -equivalente da seguinte função de teste zsh ?

testfn () {
  printf '>%s<\n' $@[2,$#]
}

% testfn a b c d
>b<
>c<
>d<

Abaixo está o mais próximo que consegui chegar; como mencionado acima, requer a atribuição de $@ a uma variável intermediária:

testfn () {
  holdargs=( $@ )
  printf '>%s<\n' "${holdargs[@]:1}"
}

Todas as minhas tentativas em algo que não exigem uma variável intermediária falham com um erro bad substitution .

    
por kjo 10.08.2013 / 13:58

2 respostas

5

Isso funcionou para mim:

testfn () {
   printf '>%s<\n' "${@:2}"
}

Exemplo:

$ testfn a b c d
>b<
>c<
>d<

De Guia do Bash Beaner

$@     Expands to the positional parameters, starting from one. When the 
       expansion occurs within double quotes, each parameter expands to a
       separate word.

Dê uma olhada na seção Parâmetros Posicionais do Wiki do Bash Hacker , bem como no Mass Usage para mais exemplos e detalhes.

Em geral, quando você aspas duplas de $@ assim, "$@" ou "${@:2}" os resultados retornados serão resultados de aspas duplas.

Elementos de matrizes como $@ podem ser acessados usando esta notação:

"${@:START:COUNT}"

trecho da seção Uso em massa

This will expand COUNT number of positional parameters starting at START. COUNT can be omitted (${@:START}), in this case all positional parameters beginning at START are expanded.

Elementos vazios?

Outros afirmaram que o acima não é equivalente porque não pode lidar com elementos vazios. Eu estou no Bash 4.1.7 e parece que sim.

Exemplos:

$ testfn "" a "" c d e
>a<
><
>c<
>d<
>e<

$ testfn '' a '*' c d e
>a<
>*<
>c<
>d<
>e<

$ testfn "" a " " c d e
>a<
> <
>c<
>d<
>e<
    
por 10.08.2013 / 17:05
3

Em zsh ,

printf '<%s>\n' ${@[2,$#]}

mesmo que

printf '<%s>\n' $@[2,-1]

imprime os elementos posicionais não vazios 2 ao último. É o mesmo que escrever (se $ # = = 5):

printf '<%s>\n' $2 $3 $4 $5

Então:

$ set 1 2 '' '*' 5
$ printf '<%s>\n' $@[2,-1]
<2>
<*>
<5>

Para obter o equivalente em bash , você precisa:

$ set 1 2 '' '*' 5
$ a=("$@")
$ IFS=; set -f
$ printf '<%s>\n' ${a[@]:1}
<2>
<*>
<5>

Você precisa de um array intermediário porque ${@:2} não funciona dessa forma (pelo menos não com 4.2.45).

Claro, se você quisesse todos os elementos, mas o primeiro, independentemente de estar vazio ou não, você teria que escrevê-lo:

$ printf '<%s>\n' "$@[2,-1]"
<2>
<>
<*>
<5>

em zsh e

$ printf '<%s>\n' "${@:2}"
<2>
<>
<*>
<5>

em bash .

Observe que zsh incluiu a sintaxe ${array:first:n} (somente quando não está em conflito com os modificadores de estilo csh), portanto o código bash (na verdade, ksh) também funcionará em versões mais recentes de zsh . / p>

Quanto ao motivo da discrepância entre ${a[@]:1} versus ${@:2} , você deve ter em mente que, em bash , ao contrário de zsh ou csh ou rc , mas como ksh onde bash copiou seus arrays, arrays são esparsos e índices começam em 0.

${a[@]:4:5} retorna os 5 primeiros elementos cujo índice é maior ou igual a 4. O primeiro elemento em $@ tem indice 1 ( $1 ), enquanto um array definido como a=(...) tem seus elementos definidos com índices começando em 0.

Bem, isso não é exatamente verdade. Em bash , "$@" expande como "$1" "$2" "$3" "$4" "$5" , enquanto "${@:0:1}" é como "$0" se e somente se $ # > 0. "$@" parece ser como "${@:1}" , enquanto $@ (sem as aspas) é apenas como ${@:1} se $IFS não estiver definido ou não estiver vazio. Isso soa como insetos para mim. O comportamento em ksh é diferente e, mais uma vez, consistente.

    
por 10.08.2013 / 17:16