Was bedeutet $# in Bash?

Was bedeutet $# in Bash?

Ich habe ein Skript in einer Datei mit dem Namen Instanz:

echo "hello world"
echo ${1}

Und wenn ich dieses Skript ausführe mit:

./instance solfish

Ich erhalte diese Ausgabe:

hello world
solfish

Aber wenn ich laufe:

echo $# 

Da steht "0". Warum? Ich verstehe nicht, was das $#bedeutet.

Bitte erklären Sie es.

Antwort1

$#ist eine spezielle Variable in bash, die auf die Anzahl der Argumente (Positionsparameter) erweitert wird, d. h. $1, $2 ...an das betreffende Skript oder die Shell übergeben wird, wenn das Argument direkt an die Shell übergeben wird, z. B. in bash -c '...' .....

Dies ist ähnlich wie argcin C.


Vielleicht wird es dadurch klarer:

$ bash -c 'echo $#'
0

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

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

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

Beachten Sie, dass bash -cdas Argument nach dem folgenden Befehl, beginnend bei 0 (, angenommen wird $0; technisch gesehen ist dies nur bashdie Möglichkeit von , Ihnen das Festlegen zu ermöglichen $0, und nicht wirklich ein Argument). Es _wird hier also nur als Platzhalter verwendet; die tatsächlichen Argumente sind x( $1), y( $2) und z( $3).


Gleiches gilt für Ihr Skript (vorausgesetzt script.sh), wenn Sie Folgendes haben:

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

Wenn Sie dies tun, gilt Folgendes:

./script.sh foo bar

Das Skript gibt 2 aus.

./script.sh foo

gibt 1 aus.

Antwort2

echo $#gibt die Anzahl der Positionsparameter Ihres Skripts aus.

Sie haben keine, also wird 0 ausgegeben.

echo $#ist innerhalb des Skripts nützlich, nicht als separater Befehl.

Wenn Sie ein Skript mit einigen Parametern ausführen wie

./instance par1 par2

Das echo $#Einfügen in das Skript ergibt die Ausgabe 2.

Antwort3

$#wird normalerweise in Bash-Skripten verwendet, um sicherzustellen, dass ein Parameter übergeben wird. Im Allgemeinen prüfen Sie am Anfang Ihres Skripts, ob ein Parameter übergeben wurde.

Hier ist beispielsweise ein Ausschnitt aus einem Skript, an dem ich heute gearbeitet habe:

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

Um $#die Anzahl der an ein Skript übergebenen Parameter zusammenzufassen, wird Folgendes gemeldet. In Ihrem Fall haben Sie keine Parameter übergeben und das gemeldete Ergebnis lautet 0:


Andere #Verwendungen in Bash

Dies #wird in Bash häufig verwendet, um die Anzahl der Vorkommen oder die Länge einer Variablen zu zählen.

So ermitteln Sie die Länge einer Zeichenfolge:

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

kehrt zurück:11

So ermitteln Sie die Anzahl der Array-Elemente:

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

kehrt zurück:3

So ermitteln Sie die Länge des ersten Array-Elements:

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

Gibt zurück: 1(Die Länge von A, 0 ist das erste Element, da Arrays nullbasierte Indizes/Indizes verwenden).

Antwort4

$#ist die Anzahl der Argumente. Denken Sie jedoch daran, dass es in einer Funktion anders sein wird.

$#ist die Anzahl der Positionsparameter, die an das Skript, die Shell,oder Shell-Funktion. Dies liegt daran, dass während der Ausführung einer Shell-Funktion diePositionsparameter werden vorübergehend durch die Argumente der Funktion ersetzt. Dadurch können Funktionen ihre eigenen Positionsparameter akzeptieren und verwenden.

Dieses Skript druckt immer 3, unabhängig davon, wie viele Argumente an das Skript selbst übergeben wurden, da "$#"die Funktion fauf die Anzahl der an die Funktion übergebenen Argumente erweitert wird:

#!/bin/sh

f() {
    echo "$#"
}

f a b c

Dies ist wichtig, da es bedeutet, dass Code wie dieser nicht wie erwartet funktioniert, wenn Sie nicht mit der Funktionsweise von Positionsparametern in Shell-Funktionen vertraut sind:

#!/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_argswird $#auf die Anzahl der an die Funktion selbst übergebenen Argumente erweitert, die in diesem Skript immer 0 ist.

Wenn Sie eine solche Funktionalität in einer Shell-Funktion wünschen, müssten Sie stattdessen so etwas schreiben:

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

Das funktioniert, weil $#erweitert wirddraußendie Funktion und an die Funktion übergeben als einer vones istPositionsparameter. Innerhalb der Funktion $1wird es zum ersten Positionsparameter erweitert, der an die Shell-Funktion übergeben wurde, und nicht an das Skript, zu dem es gehört.

Daher beziehen sich $#die speziellen Parameter $1, $2, usw. sowie und wie $@auch $*auf die an eine Funktion übergebenen Argumente, wenn sie in der Funktion erweitert werden.$0nichtÄnderung am Namen der Funktion, weshalb ich damit trotzdem eine hochwertige Fehlermeldung erzeugen konnte.

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

Wenn Sie eine Funktion innerhalb einer anderen definieren, arbeiten Sie analog dazu mit den Positionsparametern, die an die innerste Funktion übergeben werden, in der die Erweiterung durchgeführt wird:

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

Ich habe dieses Skript aufgerufen nestedund (nach dem Ausführen chmod +x nested) ausgeführt:

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

Ja, ich weiß. „1 Argumente“ ist ein Pluralisierungsfehler.

Auch die Positionsparameter können verändert werden.

Wenn Sie ein Skript schreiben, sind die Positionsparameter außerhalb einer Funktion die Befehlszeilenargumente, die an das Skript übergeben werden.es sei denn, Sie haben sie geändert.

Eine gängige Möglichkeit, sie zu ändern, ist die Verwendung des shiftintegrierten Parameters, der jeden Positionsparameter um eins nach links verschiebt, den ersten entfernt und $#um 1 verringert:

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

Sie können auch mit dem integrierten Tool geändert werden set:

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

verwandte Informationen