O que $# significa no bash?

O que $# significa no bash?

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 argca 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 -crecebe o argumento após o comando a partir de 0 ( $0; tecnicamente, é apenas bashuma 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 fse 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, $1expande 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,, $2etc., assim como $@e $*, também pertencem aos argumentos passados ​​para uma função, quando são expandidos na função. No entanto, $0faznã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 nestede (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 shiftbuiltin, 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 setintegrado:

#!/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

informação relacionada