Para apenas festança , a resposta de Kevin Little propõe o simples ${!#}. Teste usando bash -c 'echo ${!#}' arg1 arg2 arg3. Para bash , ksh e zsh , a resposta de Dennis Williamson propõe ${@: -1}. Além disso, ${*: -1}também pode ser usado. Teste usando zsh -c 'echo ${*: -1}' arg1 arg2 arg3. Mas isso não funciona para o dash , csh e tcsh .
Olibre
2
${!#}, ao contrário ${@: -1}, também funciona com a expansão de parâmetros. Você pode testá-lo com bash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out.
Arch Stanton
Respostas:
177
Isso é meio que um hack:
for last;do true;done
echo $last
Este também é bastante portátil (novamente, deve funcionar com bash, ksh e sh) e não muda os argumentos, o que poderia ser bom.
Ele usa o fato de forimplicitamente repetir os argumentos se você não informar o que fazer loop e o fato de que as variáveis de loop não têm escopo definido: elas mantêm o último valor para o qual foram definidas.
Para aqueles (como eu) se perguntando por que o espaço é necessário, o man bash tem o que dizer sobre isso:> Observe que um deslocamento negativo deve ser separado do cólon por pelo menos um espaço para evitar ser confundido com a expansão: -.
foo
1
Eu tenho usado isso e ele quebra no MSYS2 bash apenas no Windows. Bizarro.
Steven Lu
2
Nota: Esta resposta funciona para todas as matrizes Bash, diferente da ${@:$#}que funciona apenas $@. Se você fosse copiar $@para uma nova matriz com arr=("$@"), ${arr[@]:$#}seria indefinido. Isso ocorre porque $@possui um 0º elemento que não está incluído nas "$@"expansões.
Mr. Llama
@ Mr.Llama: Outro lugar a evitar $#é ao iterar sobre matrizes, pois, no Bash, as matrizes são escassas e, embora $#mostrem o número de elementos na matriz, não estão necessariamente apontando para o último elemento (ou elemento + 1). Em outras palavras, não se deve fazer for ((i = 0; i++; i < $#)); do something "${array[$i]}"; donee, em vez disso, fazer for element in "${array[@]}"; do something "$element"; doneou iterar sobre os índices: for index in "${!array[@]}"; do something "$index" "${array[$index]}"; donese você precisar fazer algo com os valores dos índices.
Pausado até novo aviso.
80
$ set quick brown fox jumps
$ echo ${*:-1:1}# last argument
jumps
$ echo ${*:-1}# or simply
jumps
$ echo ${*:-2:1}# next to last
fox
O espaço é necessário para que ele não seja interpretado como um valor padrão .
Melhor resposta, pois também inclui o penúltimo argumento. Obrigado!
e40
1
Steven, não sei o que você fez para desembarcar na Caixa de Penalidades, mas estou amando seu trabalho aqui.
Bruno Bronosky
4
sim. simplesmente o melhor. tudo menos comando e último argumento ${@: 1:$#-1}
Dyno Fu
1
@DynoFu obrigado por isso, você respondeu minha próxima pergunta. Portanto, uma mudança pode parecer:, echo ${@: -1} ${@: 1:$#-1}onde a última se torna a primeira e o resto desliza para baixo #
Mike
73
A resposta mais simples para o bash 3.0 ou superior é
_last=${!#}# *indirect reference* to the $# variable# or
_last=$BASH_ARGV # official built-in (but takes more typing :)
É isso aí.
$ cat lastarg
#!/bin/bash# echo the last arg given:
_last=${!#}
echo $_last
_last=$BASH_ARGV
echo $_last
for x;do
echo $x
done
$BASH_ARGVnão funciona dentro de uma função bash (a menos que eu esteja fazendo algo errado).
Big McLargeHuge
1
O BASH_ARGV possui os argumentos quando o bash foi chamado (ou para uma função) e não a lista atual de argumentos posicionais.
Isaac
Observe também o BASH_ARGVque lhe renderá é o valor que foi o último argumento que foi dado, em vez de simplesmente "o último valor". Por exemplo !: se você fornecer um único argumento, então chamar shift, ${@:$#}não produzirá nada (porque você alterou o primeiro e único argumento!), No entanto, BASH_ARGVainda fornecerá esse último argumento (anteriormente).
O seguinte funcionará para você. O @ é para matriz de argumentos. : significa às. $ # é o comprimento da matriz de argumentos. Portanto, o resultado é o último elemento:
${@:$#}
Exemplo:
function afunction{
echo ${@:$#} }
afunction -d -o local50#Outputs 50
Enquanto o exemplo é para uma função, os scripts também funcionam da mesma maneira. Eu gosto desta resposta porque é clara e concisa.
Jonah Braun
1
E não é hacky. Ele usa recursos explícitos da linguagem, não efeitos colaterais ou qwerks especiais. Essa deve ser a resposta aceita.
musicin3d
25
Foi encontrado ao procurar separar o último argumento de todos os anteriores. Embora algumas das respostas recebam o último argumento, elas não ajudam muito se você precisar de todos os outros argumentos também. Isso funciona muito melhor:
A resposta de Steven Penny é um pouco melhor: use ${@: -1}para o último e o segundo${@: -2:1} para o último (e assim por diante ...). Exemplo: bash -c 'echo ${@: -1}' prog 0 1 2 3 4 5 6imprime 6. Para ficar com a abordagem atual do AgileZebra, use ${@:$#-1:1}para obter o segundo último . Exemplo: bash -c 'echo ${@:$#-1:1}' prog 0 1 2 3 4 5 6imprime 5. (e ${@:$#-2:1}para obter o terceiro último e assim por diante ...)
olibre
4
A resposta do AgileZebra fornece uma maneira de obter todos os argumentos, exceto os últimos, para que eu não diga que a resposta de Steven a substitui. No entanto, parece não haver razão $((...))para subtrair o 1, você pode simplesmente usá-lo ${@:1:$# - 1}.
dkasak
Obrigado dkasak. Atualizado para refletir sua simplificação.
AgileZebra
22
Isso funciona em todos os shells compatíveis com POSIX:
Veja este comentário anexado a uma resposta idêntica mais antiga.
Pausado até novo aviso.
1
A solução portátil mais simples que vejo aqui. Este não tem nenhum problema de segurança, @DennisWilliamson, a citação parece empiricamente correta, a menos que haja uma maneira de definir $#uma sequência arbitrária (acho que não). eval last=\"\${$#}\"também funciona e está obviamente correto. Não veja por que as aspas não são necessárias.
Palec 17/10
1
Para bash , zsh , dash e ksheval last=\"\${$#}\" são bons. Mas para csh e tcsh uso eval set last=\"\${$#}\". Veja este exemplo: tcsh -c 'eval set last=\"\${$#}\"; echo "$last"' arg1 arg2 arg3.
olibre
12
Aqui está a minha solução:
bastante portátil (todos os POSIX sh, bash, ksh, zsh) devem funcionar
Esta é uma ótima idéia, mas tenho algumas sugestões: Primeiro, citar deve ser adicionado ao redor "$1"e "$#"(veja esta ótima resposta unix.stackexchange.com/a/171347 ). Em segundo lugar, echoinfelizmente não é portátil (principalmente para -n), então printf '%s\n' "$1"deve ser usado.
Joki
thanks @joki Eu trabalhei com muitos sistemas unix diferentes e também não confiava echo -n, no entanto, não conheço nenhum sistema posix echo "$1"que falhe. De qualquer forma, printfé realmente mais previsível - atualizado.
Michał Šrajer
@ MichałŠrajer considere o caso em que "$ 1" é "-n" ou "-", portanto, por exemplo, ntharg 1 -nou ntharg 1 --pode gerar resultados diferentes em vários sistemas. O código que você tem agora é seguro!
Joki
10
Das soluções mais antigas às mais recentes:
A solução mais portátil, ainda mais antiga sh(funciona com espaços e caracteres glob) (sem loop, mais rápido):
eval printf "'%s\n'""\"\${$#}\""
Desde a versão 2.01 do bash
$ set--The quick brown fox jumps over the lazy dog
$ printf '%s\n'"${!#} ${@:(-1)} ${@: -1} ${@:~0} ${!#}"
dog dog dog dog dog
Para ksh, zsh e bash:
$ printf '%s\n'"${@: -1} ${@:~0}"# the space beetwen `:`# and `-1` is a must.
dog dog
E para "próximo ao último":
$ printf '%s\n'"${@:~1:1}"
lazy
Usando printf para solucionar problemas com argumentos que começam com um traço (como -n ).
Para todas as conchas e para as mais antigas sh(funciona com espaços e caracteres glob) é:
$ set--The quick brown fox jumps over the lazy dog "the * last argument"
$ eval printf "'%s\n'""\"\${$#}\""The last * argument
Ou, se você deseja definir um lastvar:
$ eval last=\${$#}; printf '%s\n' "$last"The last * argument
shift $(($# - 1))- não há necessidade de um utilitário externo. Funciona em Bash, ksh, zsh e dash.
Pausado até novo aviso.
@Dennis: Legal! Eu não sabia sobre a $((...))sintaxe.
Laurence Gonsalves 11/11
Você deseja usar printf '%s\n' "$1"para evitar comportamentos inesperados de echo(por exemplo, para -n).
Joki #
2
Se você deseja fazer isso de maneira não destrutiva, uma maneira é passar todos os argumentos para uma função e retornar o último:
#!/bin/bash
last(){if[[ $# -ne 0 ]] ; then
shift $(expr $# - 1)
echo "$1"#else#do something when no argumentsfi}
lastvar=$(last "$@")
echo $lastvar
echo "$@"
pax>./qq.sh 123 a b
b
123 a b
Se você realmente não se importa em manter os outros argumentos, não precisa disso em uma função, mas é difícil pensar em uma situação em que você nunca iria querer manter os outros argumentos, a menos que eles já tenham sido processados, nesse caso, eu usaria o método process / shift / process / shift / ... para processá-los sequencialmente.
Estou assumindo aqui que você deseja mantê-los porque não seguiu o método seqüencial. Este método também lida com o caso em que não há argumentos, retornando "". Você pode ajustar esse comportamento facilmente inserindo a elsecláusula comentada .
A avaliação para referência indireta é um exagero, para não mencionar práticas inadequadas, e uma grande preocupação de segurança (o valor não é citado em eco ou fora de $ (). O Bash possui uma sintaxe interna para referências indiretas, para qualquer var: !portanto last="${!#}", usaria o mesmo abordagem (referência indireta em $ #) de uma maneira muito mais segura, compacta, embutida e sã. E corretamente citada.
MestreLion
Veja uma maneira mais limpa de executar o mesmo em outra resposta .
Palec 17/10
As cotações do @MestreLion não são necessárias no RHS de =.
Tom Hale
1
@ TomHale: true para o caso particular de ${!#}, mas não em geral: aspas ainda são necessárias se o conteúdo contiver espaço em branco literal, como last='two words'. Somente o $()espaço em branco é seguro, independentemente do conteúdo.
MestreLion 18/06/19
1
Depois de ler as respostas acima, escrevi um shell script de Q&D (deve funcionar com sh e bash) para executar o g ++ no PGM.cpp para produzir a imagem executável PGM. Ele pressupõe que o último argumento na linha de comando seja o nome do arquivo (.cpp é opcional) e todos os outros argumentos são opções.
#!/bin/shif[ $# -lt 1 ]then
echo "Usage: `basename $0` [opt] pgm runs g++ to compile pgm[.cpp] into pgm"
exit 2fi
OPT=
PGM=# PGM is the last argument, all others are considered optionsfor F;do OPT="$OPT $PGM"; PGM=$F;done
DIR=`dirname $PGM`
PGM=`basename $PGM .cpp`# put -o first so it can be overridden by -o specified in OPTset-x
g++-o $DIR/$PGM $OPT $DIR/$PGM.cpp
A abordagem de avaliação já foi apresentada aqui muitas vezes, mas esta tem uma explicação de como funciona. Pode ser melhorado ainda mais, mas ainda vale a pena mantê-lo.
Eu suspeito que isso iria falhar com ./arguments.sh "último valor"
Thomas
Obrigado por verificar Thomas, tentei executar o script as como você mencionou # ./arguments.sh "last value" O último argumento é: o valor está funcionando bem agora. # ./arguments.sh "último valor, verifique com o dobro" O último argumento é: double
Ranjithkumar T
O problema é que o último argumento foi 'último valor', e não valor. O erro é causado pelo argumento que contém um espaço.
Thomas
0
Existe uma maneira muito mais concisa de fazer isso. Argumentos para um script bash podem ser trazidos para uma matriz, o que torna muito mais fácil lidar com os elementos. O script abaixo sempre imprimirá o último argumento passado para um script.
argArray=("$@")# Add all script arguments to argArray
arrayLength=${#argArray[@]}# Get the length of the array
lastArg=$((arrayLength -1))# Arrays are zero based, so last arg is -1
echo ${argArray[$lastArg]}
${!#}
. Teste usandobash -c 'echo ${!#}' arg1 arg2 arg3
. Para bash , ksh e zsh , a resposta de Dennis Williamson propõe${@: -1}
. Além disso,${*: -1}
também pode ser usado. Teste usandozsh -c 'echo ${*: -1}' arg1 arg2 arg3
. Mas isso não funciona para o dash , csh e tcsh .${!#}
, ao contrário${@: -1}
, também funciona com a expansão de parâmetros. Você pode testá-lo combash -c 'echo ${!#%.*}' arg1.out arg2.out arg3.out
.Respostas:
Isso é meio que um hack:
Este também é bastante portátil (novamente, deve funcionar com bash, ksh e sh) e não muda os argumentos, o que poderia ser bom.
Ele usa o fato de
for
implicitamente repetir os argumentos se você não informar o que fazer loop e o fato de que as variáveis de loop não têm escopo definido: elas mantêm o último valor para o qual foram definidas.fonte
true
faz parte do POSIX .for last in "$@"; do :; done
também torna a intenção muito mais clara.Isso é apenas Bash:
fonte
${@:$#}
que funciona apenas$@
. Se você fosse copiar$@
para uma nova matriz comarr=("$@")
,${arr[@]:$#}
seria indefinido. Isso ocorre porque$@
possui um 0º elemento que não está incluído nas"$@"
expansões.$#
é ao iterar sobre matrizes, pois, no Bash, as matrizes são escassas e, embora$#
mostrem o número de elementos na matriz, não estão necessariamente apontando para o último elemento (ou elemento + 1). Em outras palavras, não se deve fazerfor ((i = 0; i++; i < $#)); do something "${array[$i]}"; done
e, em vez disso, fazerfor element in "${array[@]}"; do something "$element"; done
ou iterar sobre os índices:for index in "${!array[@]}"; do something "$index" "${array[$index]}"; done
se você precisar fazer algo com os valores dos índices.O espaço é necessário para que ele não seja interpretado como um valor padrão .
Observe que isso é apenas para o bash.
fonte
${@: 1:$#-1}
echo ${@: -1} ${@: 1:$#-1}
onde a última se torna a primeira e o resto desliza para baixo #A resposta mais simples para o bash 3.0 ou superior é
É isso aí.
A saída é:
fonte
$BASH_ARGV
não funciona dentro de uma função bash (a menos que eu esteja fazendo algo errado).BASH_ARGV
que lhe renderá é o valor que foi o último argumento que foi dado, em vez de simplesmente "o último valor". Por exemplo !: se você fornecer um único argumento, então chamar shift,${@:$#}
não produzirá nada (porque você alterou o primeiro e único argumento!), No entanto,BASH_ARGV
ainda fornecerá esse último argumento (anteriormente).Use a indexação combinada com o comprimento de:
Observe que isso é apenas para o bash.
fonte
echo ${@:$#}
O seguinte funcionará para você. O @ é para matriz de argumentos. : significa às. $ # é o comprimento da matriz de argumentos. Portanto, o resultado é o último elemento:
Exemplo:
Observe que isso é apenas para o bash.
fonte
Foi encontrado ao procurar separar o último argumento de todos os anteriores. Embora algumas das respostas recebam o último argumento, elas não ajudam muito se você precisar de todos os outros argumentos também. Isso funciona muito melhor:
Observe que isso é apenas para o bash.
fonte
${@: -1}
para o último e o segundo${@: -2:1}
para o último (e assim por diante ...). Exemplo:bash -c 'echo ${@: -1}' prog 0 1 2 3 4 5 6
imprime6
. Para ficar com a abordagem atual do AgileZebra, use${@:$#-1:1}
para obter o segundo último . Exemplo:bash -c 'echo ${@:$#-1:1}' prog 0 1 2 3 4 5 6
imprime5
. (e${@:$#-2:1}
para obter o terceiro último e assim por diante ...)$((...))
para subtrair o 1, você pode simplesmente usá-lo${@:1:$# - 1}
.Isso funciona em todos os shells compatíveis com POSIX:
Fonte: http://www.faqs.org/faqs/unix-faq/faq/part2/section-12.html
fonte
$#
uma sequência arbitrária (acho que não).eval last=\"\${$#}\"
também funciona e está obviamente correto. Não veja por que as aspas não são necessárias.eval last=\"\${$#}\"
são bons. Mas para csh e tcsh usoeval set last=\"\${$#}\"
. Veja este exemplo:tcsh -c 'eval set last=\"\${$#}\"; echo "$last"' arg1 arg2 arg3
.Aqui está a minha solução:
maleval
Código:
fonte
"$1"
e"$#"
(veja esta ótima resposta unix.stackexchange.com/a/171347 ). Em segundo lugar,echo
infelizmente não é portátil (principalmente para-n
), entãoprintf '%s\n' "$1"
deve ser usado.echo -n
, no entanto, não conheço nenhum sistema posixecho "$1"
que falhe. De qualquer forma,printf
é realmente mais previsível - atualizado.ntharg 1 -n
ountharg 1 --
pode gerar resultados diferentes em vários sistemas. O código que você tem agora é seguro!Das soluções mais antigas às mais recentes:
A solução mais portátil, ainda mais antiga
sh
(funciona com espaços e caracteres glob) (sem loop, mais rápido):Desde a versão 2.01 do bash
Para ksh, zsh e bash:
E para "próximo ao último":
Usando printf para solucionar problemas com argumentos que começam com um traço (como
-n
).Para todas as conchas e para as mais antigas
sh
(funciona com espaços e caracteres glob) é:Ou, se você deseja definir um
last
var:E para "próximo ao último":
fonte
Se você estiver usando o Bash> = 3.0
fonte
echo ${BASH_ARGV[1]}
ARGV
significa "vetor de argumento".Pois
bash
, este comentário sugeriu o muito elegante:Como um bônus, isso também funciona
zsh
.fonte
Isso muda os argumentos pelo número de argumentos menos 1 e retorna o primeiro (e único) argumento restante, que será o último.
Eu testei apenas no bash, mas deve funcionar em sh e ksh também.
fonte
shift $(($# - 1))
- não há necessidade de um utilitário externo. Funciona em Bash, ksh, zsh e dash.$((...))
sintaxe.printf '%s\n' "$1"
para evitar comportamentos inesperados deecho
(por exemplo, para-n
).Se você deseja fazer isso de maneira não destrutiva, uma maneira é passar todos os argumentos para uma função e retornar o último:
Se você realmente não se importa em manter os outros argumentos, não precisa disso em uma função, mas é difícil pensar em uma situação em que você nunca iria querer manter os outros argumentos, a menos que eles já tenham sido processados, nesse caso, eu usaria o método process / shift / process / shift / ... para processá-los sequencialmente.
Estou assumindo aqui que você deseja mantê-los porque não seguiu o método seqüencial. Este método também lida com o caso em que não há argumentos, retornando "". Você pode ajustar esse comportamento facilmente inserindo a
else
cláusula comentada .fonte
Para tcsh:
Tenho certeza de que essa seria uma solução portátil, exceto pela atribuição.
fonte
Achei a resposta do @ AgileZebra (mais o comentário do @ starfry) a mais útil, mas ela é
heads
escalar. Uma matriz é provavelmente mais útil:Observe que isso é apenas para o bash.
fonte
echo "${heads[-1]}"
imprime o último elementoheads
. Ou eu estou esquecendo de alguma coisa?Uma solução usando
eval
:fonte
!
portantolast="${!#}"
, usaria o mesmo abordagem (referência indireta em $ #) de uma maneira muito mais segura, compacta, embutida e sã. E corretamente citada.=
.${!#}
, mas não em geral: aspas ainda são necessárias se o conteúdo contiver espaço em branco literal, comolast='two words'
. Somente o$()
espaço em branco é seguro, independentemente do conteúdo.Depois de ler as respostas acima, escrevi um shell script de Q&D (deve funcionar com sh e bash) para executar o g ++ no PGM.cpp para produzir a imagem executável PGM. Ele pressupõe que o último argumento na linha de comando seja o nome do arquivo (.cpp é opcional) e todos os outros argumentos são opções.
fonte
O seguinte será definido
LAST
para o último argumento sem alterar o ambiente atual:Se outros argumentos não forem mais necessários e puderem ser alterados, poderá ser simplificado para:
Por motivos de portabilidade, a seguir:
pode ser substituído por:
Substituindo também
$()
por aspas, obtemos:fonte
Agora só preciso adicionar um texto, porque minha resposta foi muito curta para postar. Preciso adicionar mais texto para editar.
fonte
$argv
mas não$#argv
-$argv[(count $argv)]
funciona em peixe).Isso faz parte da minha função de cópia:
Para usar em scripts, faça o seguinte:
Explicação (a maioria aninhada primeiro):
$(echo '$'"$#")
retorna$[nr]
onde[nr]
é o número de parâmetros. Por exemplo, a string$123
(não expandida).echo $123
retorna o valor do 123º parâmetro, quando avaliado.eval
apenas expande$123
para o valor do parâmetro, por exemplolast_arg
. Isso é interpretado como uma sequência e retornado.Funciona com o Bash em meados de 2015.
fonte
fonte
Experimente o script abaixo para encontrar o último argumento
Resultado:
Obrigado.
fonte
Existe uma maneira muito mais concisa de fazer isso. Argumentos para um script bash podem ser trazidos para uma matriz, o que torna muito mais fácil lidar com os elementos. O script abaixo sempre imprimirá o último argumento passado para um script.
Saída de amostra
fonte
Usando expansão de parâmetro (excluir início correspondente):
Também é fácil obter tudo antes do último:
fonte
Para retornar o último argumento do comando usado mais recentemente, use o parâmetro especial:
Nesse caso, ele funcionará se for usado dentro do script antes que outro comando seja chamado.
fonte
Apenas use
!$
.fonte