Cosa significa $ # in bash?


26

Ho uno script in un file chiamato istanza:

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.


10
Perché corri $#? Cosa vuoi ottenere? Dove hai ricevuto questo comando. Non è per niente rilevante.
Pilota 6

7
@ Pilot6 in questo caso chiede il significato di una variabile, questo non è un caso specifico, quindi perché l'operatore dovrebbe fornire tali informazioni?
ADDB

21
@solfish Pilot sta cercando di capire cosa ti aspettavi. Hai detto che hai corso echo $#e che è tornato 0che è normale. Sei stato sorpreso da questo, ma non spieghi cosa ti aspettavi o perché sei stato sorpreso. Quindi ci aiuterebbe a darti una risposta migliore se spiegassi cosa ti aspettavi. Quello che pensavi echo $#potesse fare. Se correvi ./instance solfishe ./instancecontenevi echo $#, sarebbe stampato 1e non 0.
terdon,


1
Java inoltre non utilizza argc.
JakeRobb,

Risposte:


66

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

Questo è simile a argcin C.


Forse questo chiarirà:

$ bash -c 'echo $#'
0

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

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

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

Nota che, bash -cprende argomento dopo il comando seguendolo a partire da 0 ( $0; tecnicamente, è solo un bashmodo per farti impostare $0, non un argomento in realtà), quindi _viene usato qui solo come segnaposto; gli argomenti effettivi sono x( $1), y( $2) e z( $3).


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

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

Quindi quando lo fai:

./script.sh foo bar

lo script produrrà 2; allo stesso modo,

./script.sh foo

produrrà 1.


1
Penso che questa risposta sia fuorviante. $ # è l'indice massimo dell'array di argomenti. Se dai un argomento, sarà disponibile a $ 0 e $ # avrà il valore 0. Se dai due argomenti, saranno tra $ 0 e $ 1 e $ # avrà il valore 1.
JakeRobb

1
Inoltre, quando si utilizza bash -c, il comportamento è diverso rispetto a quando si esegue uno script di shell eseguibile, poiché in quest'ultimo caso l'argomento con indice 0 è il comando di shell utilizzato per richiamarlo. In quanto tale, penso che il modo per correggere questa risposta sia di cambiarla per eseguire script come file invece di usarli bash -c, poiché è così che lo stava facendo il richiedente.
JakeRobb,

1
@JakeRobb No. Per uno script, $0sarebbe lo script stesso; argomenti sarebbero $1, $2, $3.... bash -cil comportamento è diverso in quanto è destinato a un uso non interattivo e gli argomenti che seguono il comando inizierebbero $0, l'ho menzionato chiaramente credo.
heemayl

5
@JakeRobb Mi hai spaventato. Se fornisci un argomento, questo sarà disponibile a $ 0 e $ # avrà il valore 0 - questo è chiaramente sbagliato.
heemayl,

2
Se metti un programma completo che lo esamina args bash -c, dovresti passare bashun nome significativo come filler per il 0 ° arg. bash -c 'echo "$@"' foo barstampa solo bar, perché "$@"non include $0, come sempre. bash -cnon "rende $ 0 uno degli arg", ti permette semplicemente di impostare "$ 0" nello stesso modo in cui esegui un programma con la execvechiamata di sistema o qualsiasi modo shell di sovrascrivere l'arg 0. $0non dovrebbe mai essere considerato "uno degli arg".
Peter Cordes,

17

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

Non ne hai, quindi genera 0.

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

Se si esegue uno script con alcuni parametri come

./instance par1 par2

il echo $# posto nello script produrrà 2.


6
Per l'esempio dell'OP: se aggiungi la riga echo $#al tuo script e poi esegui di nuovo ./instance solfishdovresti ottenere una riga di output aggiuntiva 1dato che hai fornito 1 parametro
derHugo

14

$#viene generalmente utilizzato negli script bash per garantire il passaggio di un parametro. Generalmente controlli un parametro all'inizio del tuo script.

Ad esempio, ecco un frammento di uno script a 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 riassumere i $#report, il numero di parametri passati a uno script. Nel tuo caso non hai passato parametri e il risultato riportato è 0.


Altro # usi in Bash

Il # è 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}

ritorna: 11

Per trovare il numero di elementi dell'array:

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

ritorna: 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 poiché le matrici usano indici / indici a base zero).


$# -ne 1? Perché no $# -le 0o equivalente?
Patrick Trentin,

@PatrickTrentin Perché non voglio che passino due o più parametri. Come ha detto il Capitano in "ottobre rosso": "Solo uno".
WinEunuuchs2Unix il

Quindi: "esattamente un argomento richiesto ..." nel messaggio di errore
Patrick Trentin,

@PatrickTrentin Il messaggio di errore spiega come viene utilizzato lo script con un esempio dell'uso di un argomento. Inoltre, lo script di backup viene chiamato da cron.daily, che viene configurato una sola volta da qualcuno esperto di tecnologia: askubuntu.com/questions/917562/…
WinEunuuchs2Unix

Quanto sia rilevante non lo saprei. Comunque, evviva.
Patrick Trentin,

10

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

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

Questo script viene sempre stampato 3, indipendentemente dal numero di argomenti passati allo script stesso, poiché "$#"nella funzione si fespande 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 della 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,$# espande al numero di argomenti passati alla funzione stessa, che in quello script è sempre 0.

Se vuoi tale funzionalità in una funzione shell, dovresti invece scrivere qualcosa del genere:

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

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

Così, come $#i parametri speciali $1, $2ecc, nonché $@e $*, riguardano anche gli argomenti passati ad una funzione, quando vengono espansi nella funzione. Tuttavia, $0non senza cambiare il nome della funzione, che è il motivo per cui ero ancora in grado di utilizzarlo 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 definisci una funzione all'interno di un'altra, stai 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 nestede (dopo averlo eseguito chmod +x nested) l'ho eseguito:

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

Si, 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 l' shiftintegrato, che sposta ogni parametro posizionale a sinistra di uno, facendo cadere il primo e diminuendo $#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 il setbuiltin:

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

1
Complimenti per la risposta educativa che è andata oltre ciò che è stato chiesto, ma ha risposto alle intenzioni di apprendimento del richiedente originale e di altri come me!
Ricardo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.