Qual é a melhor maneira de implementar print_last_arg
?
#!/bin/sh
print_last_arg () {
eval "echo \${$#}" # this hurts
}
print_last_arg foo bar baz
# baz
(Se assim fosse, digamos, em #!/usr/bin/zsh
vez de #!/bin/sh
saber o que fazer. Meu problema é encontrar uma maneira razoável de implementar isso #!/bin/sh
.)
EDIT: O acima é apenas um exemplo bobo. Meu objetivo não é imprimir o último argumento, mas sim ter uma maneira de me referir ao último argumento dentro de uma função shell.
EDIT2: Peço desculpas por uma pergunta tão pouco clara. Espero acertar desta vez.
Se isso fosse /bin/zsh
, em vez de /bin/sh
, eu poderia escrever algo como isto
#!/bin/zsh
print_last_arg () {
local last_arg=$argv[$#]
echo $last_arg
}
A expressão $argv[$#]
é um exemplo do que descrevi na minha primeira edição como uma maneira de se referir ao último argumento dentro de uma função shell .
Portanto, eu realmente deveria ter escrito meu exemplo original assim:
print_last_arg () {
local last_arg=$(eval "echo \${$#}") # but this hurts even more
echo $last_arg
}
... para deixar claro que o que estou procurando é uma coisa menos terrível para colocar à direita da tarefa.
Observe, no entanto, que em todos os exemplos, o último argumento é acessado de maneira não destrutiva . IOW, o acesso ao último argumento não afeta os argumentos posicionais como um todo.
fonte
var=$( eval echo \${$#})
aeval var=\${$#}
- os dois não são iguais.shift
eset -- ...
baseadas podem ser destrutivas, a menos que sejam usadas em funções onde também são inofensivas.eval "var=\${$#}"
quando comparado,var=${arr[evaled index]}
exceto que esse$#
é um valor seguro garantido. por que copiar todo o conjunto e destruí-lo quando você pode indexá-lo diretamente?Respostas:
... imprimirá a sequência
the last arg is
seguida de um <space> , o valor do último argumento e um <newline> à direita, se houver pelo menos 1 argumento, ou, para zero argumentos, não imprimirá nada.Se você fez:
... então você atribuiria o valor do último argumento à variável shell
$lastarg
se houver pelo menos 1 argumento ou não faria nada. De qualquer forma, você faria isso com segurança, e deve ser portátil até para você, olde Bourne, eu acho.Aqui está mais um que funcionam de forma semelhante, embora ele requer copiar a matriz arg toda duas vezes (e requer um
printf
no$PATH
para o shell Bourne) :fonte
${1+":"} return
imediatamente - porque quem quer que ela faça alguma coisa ou arrisque efeitos colaterais de qualquer tipo se não for necessária? A matemática é a mesma coisa - se você tiver certeza de que pode expandir um número inteiro positivo, pode oeval
dia inteiro.$OPTIND
é ótimo para isso.Aqui está uma maneira simplista:
(atualizado com base no argumento de @ cuonglm de que o original falhou quando não passou argumentos; isso agora ecoa uma linha em branco - altere esse comportamento na
else
cláusula, se desejado)fonte
echo "$# - 1" | bc
Dado o exemplo do post de abertura (argumentos posicionais sem espaços):
Para o padrão
IFS=' \t\n'
, que tal:Para uma expansão mais segura de
"$*"
, defina IFS (por @ StéphaneChazelas):Mas o acima falhará se seus argumentos posicionais puderem conter espaços. Nesse caso, use isso:
Observe que essas técnicas evitam o uso
eval
e não têm efeitos colaterais.Testado em shellcheck.net
fonte
$IFS
é um espaço.IFS=' '
aqui apenas para deixar claro que está sendo usado para a expansão de"$*"
.Embora essa pergunta tenha pouco mais de 2 anos, pensei em compartilhar uma opção de compactação.
Vamos correr
Expansão de parâmetros do shell Bash .
Editar
Ainda mais conciso:
echo "${@: -1}"
(Cuidado com o espaço)
Fonte
Testado no macOS 10.12.6, mas também deve retornar o último argumento na maioria dos sabores * nix disponíveis ...
Dói muito menos
¯\_(ツ)_/¯
fonte
echo "${*: -1}"
queshellcheck
não vai reclamar.${array:n:m:}
é uma extensão. (a questão foi explicitamente mencionado/bin/sh
)POSIXly:
(Essa abordagem também funciona no shell Bourne antigo)
Com outras ferramentas padrão:
(Isso não funcionará com o antigo
awk
, que não tinhaARGV
)fonte
awk
também pode ser o velho awk que não tinhaARGV
.awk
abordagem, ou use outrofor
loop simples como outro, ou passe-os para uma função.Isso deve funcionar com qualquer shell compatível com POSIX e também com o shell Solaris Bourne anterior ao POSIX:
e aqui está uma função baseada na mesma abordagem:
PS: não me diga que eu esqueci o aparelho em volta do corpo da função ;-)
fonte
in ...
de umfor
loop e a ausência de chaves em torno de umaif
instrução usada como corpo da função. Vejafor_clause
ecompound_command
em pubs.opengroup.org/onlinepubs/9699919799 .Do "Unix - Perguntas freqüentes"
(1)
Se o número de argumentos puder ser zero, o argumento zero
$0
(geralmente o nome do script) será atribuído$last
. Essa é a razão do if.2)
(3)
Para evitar a impressão de uma linha vazia quando não houver argumentos, substitua
echo "$last"
por:Uma contagem de argumento zero é evitada por
if [ $# -gt 0 ]
.Esta não é uma cópia exata do conteúdo do link na página; algumas melhorias foram adicionadas.
fonte
Isso deve funcionar em todos os sistemas com o
perl
instalado (portanto, a maioria dos UNICES):O truque é usar
printf
para adicionar um\0
argumento depois de cada shell e, em seguidaperl
, o-0
switch que define seu separador de registros como NULL. Em seguida, iteramos sobre a entrada, removemos\0
e salvamos cada linha terminada em NULL como$l
. OEND
bloco (que é o que}{
é) será executado depois que todas as entradas tiverem sido lidas e, portanto, imprimirá a última "linha": o último argumento do shell.fonte
sed
,awk
ouperl
então ele poderia ser informação valiosa de qualquer maneira. ainda assim, você pode fazer o mesmo comsed -z
versões atualizadas do GNU.Aqui está uma versão usando recursão. Não faço ideia de como isso é compatível com POSIX ...
fonte
Um conceito simples, sem aritmética, nenhum laço, nenhuma eval , apenas funções.
Lembre-se de que o shell Bourne não possuía aritmética (necessário externo
expr
). Se você deseja obter uma aritmética livre,eval
livre escolha, esta é uma opção. Necessitar de funções significa SVR3 ou superior (sem substituição de parâmetros).Veja abaixo uma versão mais robusta com printf.
Esta estrutura de chamada
printlast
é fixa , os argumentos precisam ser definidas na lista de argumentos shell$1
,$2
etc. (a pilha de argumento) e o chamado feito como dado.Se a lista de argumentos precisar ser alterada, apenas defina-os:
Ou crie uma função mais fácil de usar (
getlast
) que possa permitir argumentos genéricos (mas não tão rápido, os argumentos são passados duas vezes).Observe que os argumentos (de
getlast
, ou todos incluídos no$@
printlast) podem ter espaços, novas linhas, etc. Mas não NUL.Melhor
Esta versão não é impressa
0
quando a lista de argumentos está vazia e usa o printf mais robusto (volte paraecho
se externoprintf
não estiver disponível para shells antigos).Usando EVAL.
Se o shell Bourne for ainda mais antigo e não houver funções, ou se, por algum motivo, usar eval for a única opção:
Para imprimir o valor:
Para definir o valor em uma variável:
Se isso precisar ser feito em uma função, os argumentos deverão ser copiados para a função:
fonte
eval
?for loop
ouwhile loop
?echo "Hello"
tem loops, pois a CPU precisa ler os locais de memória em um loop. Novamente: meu código não possui loops adicionados.for
,while
ouuntil
loops. Você vê algumfor
while
ouuntil
loop escrito no código ?. Não ?, aceite o fato, aceite-o e aprenda.Este comentário sugeriu usar o muito elegante:
fonte