
Eu tenho um script em um arquivo chamado instância:
echo "hello world"
echo ${1}
E quando executo este script usando:
./instance solfish
Eu recebo esta saída:
hello world
solfish
Mas quando eu corro:
echo $#
Diz "0". Por que? Eu não entendo o que $#
significa.
Por favor, explique.
Responder1
$#
é uma variável especial em bash
, que se expande para o número de argumentos (parâmetros posicionais), ou seja, $1, $2 ...
passados para o script em questão ou para o shell no caso de argumento passado diretamente para o shell, por exemplo, em bash -c '...' ....
.
Isto é semelhante argc
a C.
Talvez isso deixe claro:
$ bash -c 'echo $#'
0
$ bash -c 'echo $#' _ x
1
$ bash -c 'echo $#' _ x y
2
$ bash -c 'echo $#' _ x y z
3
Observe que, bash -c
recebe o argumento após o comando a partir de 0 ( $0
; tecnicamente, é apenas bash
uma maneira de permitir que você defina $0
, não um argumento, na verdade), então _
é usado aqui apenas como um espaço reservado; os argumentos reais são x
( $1
), y
( $2
) e z
( $3
).
Da mesma forma, no seu script (assumindo script.sh
), se você tiver:
#!/usr/bin/env bash
echo "$#"
Então, quando você fizer:
./script.sh foo bar
o script produzirá 2; da mesma maneira,
./script.sh foo
produzirá 1.
Responder2
echo $#
gera o número de parâmetros posicionais do seu script.
Você não tem nenhum, então a saída é 0.
echo $#
é útil dentro do script, não como um comando separado.
Se você executar um script com alguns parâmetros como
./instance par1 par2
o echo $#
colocado no script produzirá 2.
Responder3
$#
é normalmente usado em scripts bash para garantir que um parâmetro seja passado. Geralmente, você verifica um parâmetro no início do seu script.
Por exemplo, aqui está um trecho de um script em que estava trabalhando hoje:
if [[ $# -ne 1 ]]; then
echo 'One argument required for the file name, e.g. "Backup-2017-07-25"'
echo '.tar will automatically be added as a file extension'
exit 1
fi
Para resumir $#
informa o número de parâmetros passados para um script. No seu caso, você não passou nenhum parâmetro e o resultado relatado é 0
.
Outros #
usos no Bash
O #
é frequentemente usado no bash para contar o número de ocorrências ou o comprimento de uma variável.
Para encontrar o comprimento de uma string:
myvar="some string"; echo ${#myvar}
retorna:11
Para encontrar o número de elementos da matriz:
myArr=(A B C); echo ${#myArr[@]}
retorna:3
Para encontrar o comprimento do primeiro elemento da matriz:
myArr=(A B C); echo ${#myArr[0]}
retorna: 1
(O comprimento de A
, 0 é o primeiro elemento, pois as matrizes usam índices/subscritos baseados em zero).
Responder4
$#
é 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 shell. Isso ocorre porque, enquanto uma função shell está em execução, oparâmetros posicionais são temporariamente substituídos pelos argumentos da função. Isso permite que as funções aceitem e usem seus próprios parâmetros posicionais.
Este script sempre imprime 3
, independente de quantos argumentos foram passados para o próprio script, pois "$#"
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
Isso é importante porque significa que um código como esse não funciona como você espera, se você não estiver familiarizado com o funcionamento dos parâmetros posicionais nas funções 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...
In check_args
, $#
expande para o número de argumentos passados para a própria função, que nesse script é sempre 0.
Se você quiser tal funcionalidade em uma função shell, você terá 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 $#
é expandidoforaa função e passado para a função como um dosisso éparâmetros posicionais. Dentro da função, $1
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, assim como $#
, os parâmetros especiais $1
,, $2
etc., assim como $@
e $*
, também pertencem aos argumentos passados para uma função, quando são expandidos na função. No entanto, $0
faznãomudei para o nome da função, por isso 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, estará 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
Chamei esse script nested
e (depois de executar chmod +x nested
) executei:
$ ./nested a
script got 1 arguments
outer() got 2 arguments
inner() got 3 arguments
Sim eu sei. "1 argumento" é 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 scripta menos que você os tenha alterado.
Uma maneira comum de alterá-los é 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
integrado:
#!/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