Fala Herbert,
naquela altura do livro eu não tinha ensinado nem o for nem o eval, então vc apelou ;). Mas vamos ver o seu exemplo passando 3 parâmetros, sendo que um deles tem espaços em branco.
$ set parm1 parm2 "parâmetro três" # passando 3 parâmetros para o Shell
$ for i in $@
> do
> continue
> done
$ echo "o ultimo parâmetro é: $i"
o ultimo parâmetro é: três # Resultado errado
$ for i in "$@" #$@ entre aspas
> do
> continue
> done
$ echo "o ultimo parâmetro é: $i"
o ultimo parâmetro é: parâmetro três # Resultado correto.
Nessa parte do livro, disse que $* e $@ eram quase igual mas veríamos a diferença no capítulo 7 qdo falássemos de arrays, isto é, tb não havia explicado o uso correto de $@
Ora, vc sabe que $# tem a qtde de parâmetros passado então para listar o último a primeira vontade que dá é fazer:
$ echo $`echo $#`
Supondo que passei 3 parâmetros a minha esperança é que as crases dessem prioridade de execução ao `echo $#`, que retornaria 3 e em seguida faria um echo $3 que retornaria parâmetro três, mas veja:
$ echo $`echo $#`
$3
Isso ocorreu, pq na fase de resolução de variáveis existia somente uma variável que era $#, qdo o interpretador termina cada fase, não volta mais nela. O cmd eval é justamente para isso, ele obriga duas passadas no cmd. Na primeira ela avalia (evaluate) o que pode e na segunda executa. Assim sendo, na 1ª passada o eval gerou echo $3 e na 2ª executou este cmd, Veja:
$ eval echo $`echo $#`
parâmetro três
Isso ficaria melhor escrito se fosse:
$ eval echo \$$#
parâmetro três
Repare que echo \$$# tb gera $3 então é só dar outra passada. Já que é para fazer como ainda não ensinei, vc tb poderia ter feito:
$ echo ${!#}
parâmetro três
E essa não vou explicar pq isso já tá muito grande. O que eu esperava que vc fizesse naquele momento, com as ferramentas que já tinha ensinado era:
$ Shf=$(($#-1))
$ shift $Shf
$ echo $1
parâmetro três
ou, abreviando,
$ shift $(($# - 1))
$ echo $1