Cosa significa $ # in bash?

19

Ho uno script in un file chiamato instance:

echo "hello world"
echo ${1}

E quando eseguo questo script usando:

./instance solfish

Ottengo questo risultato:

hello world
solfish

Ma quando corro:

echo $# 

Dice "0". Perché? Non capisco cosa significhi $# .

Per favore spiegalo.

    
posta solfish 25.07.2017 - 16:26

4 risposte

58

$# è una variabile speciale in bash , che si espande al numero di argomenti (parametri posizionali) cioè $1, $2 ... passato allo script in questione o alla shell in caso di argomento passato direttamente alla shell, ad es. in bash -c '...' .... .

Questo è simile a argc in C.

Forse questo renderà chiaro:

$ bash -c 'echo $#'
0

$ bash -c 'echo $#' _ x
1

$ bash -c 'echo $#' _ x y
2

$ bash -c 'echo $#' _ x y z
3

Tieni presente che, bash -c prende argomento dopo che il comando che lo segue inizia da 0 ( $0 ; tecnicamente, è solo bash che ti permette di impostare $0 , non un argomento in realtà), quindi _ è usato qui solo come segnaposto; gli argomenti effettivi sono x ( $1 ), y ( $2 ) e z ( $3 ).

Allo stesso modo, nel tuo script (assumendo script.sh ) se hai:

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

Quindi quando lo fai:

./script.sh foo bar

lo script produrrà 2; analogamente,

./script.sh foo

emetterà 1.

    
risposta data heemayl 25.07.2017 - 16:39
16

echo $# restituisce il numero di parametri posizionali del tuo script.

Non ne hai, quindi emette 0.

echo $# è utile all'interno dello script, non come un comando separato.

Se si esegue uno script con alcuni parametri come

./instance par1 par2

il echo $# inserito nello script verrà emesso 2.

    
risposta data Pilot6 25.07.2017 - 16:29
12

$# viene in genere utilizzato negli script di bash per garantire che venga passato un parametro. Generalmente controlli un parametro all'inizio del tuo script.

Ad esempio, ecco un frammento di uno script su cui stavo lavorando oggi:

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

Per riepilogare $# viene indicato il numero di parametri passati a uno script. Nel tuo caso non hai passato nessun parametro e il risultato riportato è 0 .

Altro # utilizza in Bash

Il # viene spesso usato in bash per contare il numero di occorrenze o la lunghezza di una variabile.

Per trovare la lunghezza di una stringa:

myvar="some string"; echo ${#myvar}

restituisce: 11

Per trovare il numero di elementi dell'array:

myArr=(A B C); echo ${#myArr[@]}

restituisce: 3

Per trovare la lunghezza del primo elemento dell'array:

myArr=(A B C); echo ${#myArr[0]}

restituisce: 1 (La lunghezza di A , 0 è il primo elemento mentre gli array usano indici / pedici basati su zero).

    
risposta data WinEunuuchs2Unix 25.07.2017 - 16:44
9

$# è il numero di argomenti, ma ricorda che sarà diverso in una funzione.

$# è il numero di parametri posizionali passati a script, shell, o funzione shell . Questo perché, mentre una funzione di shell è in esecuzione, i parametri posizionali sono temporaneamente sostituiti con gli argomenti alla funzione . Ciò consente alle funzioni di accettare e utilizzare i propri parametri posizionali.

Questo script stampa sempre 3 , indipendentemente dal numero di argomenti passati allo script stesso, perché "$#" nella funzione f si espande al numero di argomenti passati alla funzione:

#!/bin/sh

f() {
    echo "$#"
}

f a b c

Questo è importante perché significa che codice come questo non funziona come ci si potrebbe aspettare, se non si ha familiarità con il funzionamento dei parametri posizionali nelle funzioni di 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 , $# si espande al numero di argomenti passati alla funzione stessa, che in quello script è sempre 0.

Se vuoi una funzionalità di questo tipo in una funzione di shell, dovresti scrivere qualcosa come questo:

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

Funziona perché $# è espansa all'esterno della funzione e passata alla funzione come uno dei suoi parametri posizionali. All'interno della funzione, $1 si espande al primo parametro posizionale passato alla funzione shell, piuttosto che allo script di cui fa parte.

Quindi, come $# , i parametri speciali $1 , $2 , ecc., nonché [email protected] e $* , si riferiscono anche agli argomenti passati a una funzione, quando sono espansi in la funzione. Tuttavia, $0 sostituisce non con il nome della funzione, motivo per cui ero ancora in grado di usarlo per produrre un messaggio di errore di qualità.

$ ./check-args-demo a b c
./check-args-demo: error: need 2 arguments, got 3

Allo stesso modo, se si definisce una funzione all'interno di un'altra, si sta lavorando con i parametri posizionali passati alla funzione più interna in cui viene eseguita l'espansione:

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

Ho chiamato questo script nested e (dopo aver eseguito chmod +x nested ) l'ho eseguito:

$ ./nested a
script got 1 arguments
outer() got 2 arguments
inner() got 3 arguments

Sì, lo so. "1 argomenti" è un bug di pluralizzazione.

I parametri posizionali possono anche essere modificati.

Se stai scrivendo uno script, i parametri posizionali all'esterno di una funzione saranno gli argomenti della riga di comando passati allo script a meno che tu non li abbia modificati .

Un modo comune per cambiarli è con shift incorporato, che sposta ciascun parametro posizionale a sinistra di uno, rilasciando il primo e riducendo $# di 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".

Possono anche essere modificati con set incorporato:

#!/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
    
risposta data Eliah Kagan 25.07.2017 - 21:17

Leggi altre domande sui tag