$#
é o número de argumentos, mas lembre-se que será diferente em uma função.
$#
é o número de parâmetros posicionais passados para o script, shell, ou função do shell . Isso ocorre porque, enquanto uma função de shell está em execução, os parâmetros posicionais são temporariamente substituídos pelos argumentos para a função . Isso permite que as funções aceitem e usem seus próprios parâmetros posicionais.
Esse script sempre imprime 3
, independentemente de quantos argumentos foram passados para o próprio script, porque "$#"
na função f
se expande para o número de argumentos passados para a função:
#!/bin/sh
f() {
echo "$#"
}
f a b c
Isto é importante porque significa que um código como este não funciona como seria de esperar, se não estiver familiarizado com o funcionamento dos parâmetros posicionais nas funções da shell:
#!/bin/sh
check_args() { # doesn't work!
if [ "$#" -ne 2 ]; then
printf '%s: error: need 2 arguments, got %d\n' "$0" "$#" >&2
exit 1
fi
}
# Maybe check some other things...
check_args
# Do other stuff...
Em check_args
, $#
expande para o número de argumentos passados para a função em si, que nesse script é sempre 0.
Se você quiser tal funcionalidade em uma função de shell, você teria que escrever algo assim:
#!/bin/sh
check_args() { # works -- the caller must pass the number of arguments received
if [ "$1" -ne 2 ]; then
printf '%s: error: need 2 arguments, got %d\n' "$0" "$1" >&2
exit 1
fi
}
# Maybe check some other things...
check_args "$#"
Isso funciona porque $#
é expandida fora da função e passada para a função como um dos seus parâmetros posicionais. Dentro da função, $1
se expande para o primeiro parâmetro posicional que foi passado para a função shell, em vez de para o script do qual faz parte.
Assim, como $#
, os parâmetros especiais $1
, $2
, etc., bem como $@
e $*
, também pertencem aos argumentos passados para uma função, quando eles são expandidos em a função. No entanto, $0
não não muda para o nome da função, e é por isso que ainda consegui usá-la para produzir uma mensagem de erro de qualidade.
$ ./check-args-demo a b c
./check-args-demo: error: need 2 arguments, got 3
Da mesma forma, se você definir uma função dentro de outra, você está trabalhando com os parâmetros posicionais passados para a função mais interna na qual a expansão é executada:
#!/bin/sh
outer() {
inner() {
printf 'inner() got %d arguments\n' "$#"
}
printf 'outer() got %d arguments\n' "$#"
inner x y z
}
printf 'script got %d arguments\n' "$#"
outer p q
Eu chamei esse script de nested
e (depois de executar chmod +x nested
) eu o executei:
$ ./nested a
script got 1 arguments
outer() got 2 arguments
inner() got 3 arguments
Sim, eu sei. "1 arguments" é um bug de pluralização.
Os parâmetros posicionais também podem ser alterados.
Se você estiver escrevendo um script, os parâmetros posicionais fora de uma função serão os argumentos da linha de comando passados para o script , a menos que você os tenha alterado .
Uma maneira comum de alterá-las é com o shift
builtin, que desloca cada parâmetro posicional para a esquerda em um, descartando o primeiro e diminuindo $#
em 1:
#!/bin/sh
while [ "$#" -ne 0 ]; do
printf '%d argument(s) remaining.\nGot "%s".\n\n' "$#" "$1"
shift
done
$ ./do-shift foo bar baz # I named the script do-shift.
3 argument(s) remaining.
Got "foo".
2 argument(s) remaining.
Got "bar".
1 argument(s) remaining.
Got "baz".
Eles também podem ser alterados com o set
builtin:
#!/bin/sh
printf '%d args: %s\n' "$#" "$*"
set foo bar baz
printf '%d args: %s\n' "$#" "$*"
$ ./set-args a b c d e # I named the script set-args.
5 args: a b c d e
3 args: foo bar baz