O que significa $ # no bash?

18

Eu tenho um script em um arquivo chamado instance:

echo "hello world"
echo ${1}

E quando eu executo este script usando:

./instance solfish

Eu recebo esta saída:

hello world
solfish

Mas quando eu corro:

echo $# 

Diz "0". Por quê? Eu não entendo o que $# significa.

Por favor explique.

    
por solfish 25.07.2017 / 16:26

4 respostas

57

$# é uma variável especial em bash , que se expande para o número de argumentos (parâmetros posicionais), ou seja, $1, $2 ... passado para o script em questão ou o shell em caso de argumento diretamente transmitido para o shell, por exemplo em código%.

Isso é semelhante a bash -c '...' .... em 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 argc aceita argumentos após o comando a partir de 0 ( bash -c ; tecnicamente, é apenas a maneira de $0 de permitir que você defina bash , não é um argumento realmente).$0 é usado aqui apenas como um espaço reservado; os argumentos reais são _ ( x ), $1 ( y ) e $2 ( z ).

Da mesma forma, no seu script (assumindo $3 ) se você tiver:

#!/usr/bin/env bash
echo "$#"

Então, quando você faz:

./script.sh foo bar

o script produzirá 2; da mesma forma,

./script.sh foo

produzirá 1.

    
por heemayl 25.07.2017 / 16:39
16

echo $# exibe o número de parâmetros posicionais do seu script.

Você não tem nenhum, então gera 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.

    
por Pilot6 25.07.2017 / 16:29
12

$# é 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 eu estava trabalhando hoje:

if [[ $# -ne 1 ]]; then
    echo 'One argument required for file name, e.g. "Backup-2017-07-25"'
    echo '.tar will automatically be added as a file extension'
    exit 1
fi

Para resumir $# relata 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 .

Outro # usa 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 tamanho 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 / índices baseados em zero).

    
por WinEunuuchs2Unix 25.07.2017 / 16:44
9

$# é 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
    
por Eliah Kagan 25.07.2017 / 21:17

Tags