Que signifie $ # dans bash?


26

J'ai un script dans un fichier nommé instance:

echo "hello world"
echo ${1}

Et quand j'exécute ce script en utilisant:

./instance solfish

J'obtiens cette sortie:

hello world
solfish

Mais quand je cours:

echo $# 

Il dit "0". Pourquoi? Je ne comprends pas ce que ça $#veut dire.

Veuillez l'expliquer.


10
Pourquoi courez-vous $#? Que veux-tu accomplir? Où avez-vous obtenu cette commande. Ce n'est pas du tout pertinent.
Pilot6

7
@ Pilot6 dans ce cas, il demande la signification d'une variable, ce n'est pas spécifique au cas, alors pourquoi l'op aurait-il besoin de donner cette information?
ADDB

21
@solfish Pilot essaie de comprendre ce que vous attendiez. Vous avez dit que vous avez couru echo $#et qu'il est revenu, 0ce qui est normal. Vous avez été surpris par cela, mais vous n'expliquez pas à quoi vous vous attendiez ni pourquoi vous avez été surpris. Cela nous aiderait donc à mieux vous répondre si vous expliquiez ce que vous attendiez. Ce que vous pensiez que echo $#cela ferait. Si vous couriez ./instance solfishet ./instanceconteniez echo $#, cela s'imprimerait 1et non 0.
terdon


1
Java n'utilise pas non plus argc.
JakeRobb

Réponses:


67

$#est une variable spéciale dans bash, qui s'étend au nombre d'arguments (paramètres positionnels) c'est- $1, $2 ...à- dire transmis au script en question ou au shell en cas d'argument directement passé au shell, par exemple dans bash -c '...' .....

Ceci est similaire à argcC.


Cela clarifiera peut-être:

$ bash -c 'echo $#'
0

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

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

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

Notez que, bash -cprend l'argument après la commande qui le suit à partir de 0 ( $0; techniquement, c'est juste bashune façon de vous laisser définir $0, pas vraiment un argument), donc _est utilisé ici juste comme un espace réservé; les arguments réels sont x( $1), y( $2) et z( $3).


De même, dans votre script (en supposant script.sh) si vous avez:

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

Ensuite, quand vous le faites:

./script.sh foo bar

le script affichera 2; également,

./script.sh foo

affichera 1.


1
Je pense que cette réponse est trompeuse. $ # est l'indice maximum du tableau d'arguments. Si vous donnez un argument, ce sera disponible à $ 0, et $ # aura la valeur 0. Si vous donnez deux arguments, ils seront en $ 0 et $ 1, et $ # aura la valeur 1.
JakeRobb

1
De plus, lorsque vous utilisez bash -c, le comportement est différent de si vous exécutez un script shell exécutable, car dans ce dernier cas, l'argument avec index 0 est la commande shell utilisée pour l'invoquer. En tant que tel, je pense que la façon de résoudre cette réponse est de la changer pour exécuter des scripts en tant que fichiers au lieu d'utiliser bash -c, car c'est ainsi que le demandeur le faisait.
JakeRobb

1
@JakeRobb Non. Pour un script, ce $0serait le script lui-même; les arguments seraient $1, $2, $3.... bash -cle comportement est différent car il est destiné à une utilisation non interactive et les arguments suivant la commande commenceraient $0, je l'ai clairement mentionné, je crois.
heemayl

5
@JakeRobb Vous m'avez mal entendu. Si vous donnez un argument, celui-ci sera disponible à $ 0, et $ # aura la valeur 0 - c'est tout à fait faux.
heemayl

2
Si vous mettez un programme complet qui le regarde dans les arguments bash -c, vous devez passer bashun nom significatif comme remplissage pour le 0ème argument. bash -c 'echo "$@"' foo barimprime uniquement bar, car "$@"ne comprend pas $0, comme toujours. bash -cne "fait pas de $ 0 l'un des arguments", il vous permet simplement de définir "$ 0" de la même manière que vous exécutez un programme avec l' execveappel système ou n'importe quel moyen shell de remplacer le 0ème argument. $0ne doit jamais être considéré comme "l'un des arguments".
Peter Cordes

17

echo $# affiche le nombre de paramètres positionnels de votre script.

Vous n'en avez aucun, il renvoie donc 0.

echo $# est utile dans le script, pas comme une commande séparée.

Si vous exécutez un script avec certains paramètres comme

./instance par1 par2

le echo $#placé dans le script affichera 2.


6
Pour l'exemple de l'OP: Si vous ajoutez la ligne echo $#à votre script et que vous exécutez à nouveau, ./instance solfishvous devriez obtenir une ligne de sortie supplémentaire avec 1puisque vous avez fourni 1 paramètre
derHugo

14

$#est généralement utilisé dans les scripts bash pour garantir la transmission d'un paramètre. Généralement, vous vérifiez un paramètre au début de votre script.

Par exemple, voici un extrait d'un script sur lequel je travaillais aujourd'hui:

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

Pour résumer les $#rapports, le nombre de paramètres transmis à un script. Dans votre cas, vous n'avez transmis aucun paramètre et le résultat signalé est 0.


Autres #utilisations dans Bash

Le #est souvent utilisé en bash pour compter le nombre d'occurrences ou la longueur d'une variable.

Pour trouver la longueur d'une chaîne:

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

résultats: 11

Pour trouver le nombre d'éléments de tableau:

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

résultats: 3

Pour trouver la longueur du premier élément du tableau:

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

renvoie: 1(La longueur de A, 0 est le premier élément car les tableaux utilisent des indices / indices de base zéro).


$# -ne 1? Pourquoi pas $# -le 0ou équivalent?
Patrick Trentin

@PatrickTrentin Parce que je ne veux pas qu'ils passent deux paramètres ou plus. Comme l'a dit le capitaine dans "Octobre rouge": "Un seul".
WinEunuuchs2Unix

Puis: "exactement un argument requis ..." dans le message d'erreur
Patrick Trentin

@PatrickTrentin Le message d'erreur explique comment le script est utilisé avec un exemple d'utilisation d'un argument. De plus, le script de sauvegarde est appelé par cron.daily qui n'est configuré qu'une seule fois par quelqu'un de technophile: askubuntu.com/questions/917562/…
WinEunuuchs2Unix

Comment cela est pertinent, je ne sais pas. Quoi qu'il en soit, bravo.
Patrick Trentin

10

$# est le nombre d'arguments, mais rappelez-vous qu'il sera différent dans une fonction.

$#est le nombre de paramètres positionnels transmis au script, au shell ou à la fonction shell . En effet, lors de l'exécution d'une fonction shell, les paramètres positionnels sont temporairement remplacés par les arguments de la fonction . Cela permet aux fonctions d'accepter et d'utiliser leurs propres paramètres de position.

Ce script s'imprime toujours 3, quel que soit le nombre d'arguments transmis au script lui-même, car "$#"dans la fonction fs'étend au nombre d'arguments transmis à la fonction:

#!/bin/sh

f() {
    echo "$#"
}

f a b c

Ceci est important car cela signifie que du code comme celui-ci ne fonctionne pas comme vous pouvez vous y attendre, si vous n'êtes pas familier avec le fonctionnement des paramètres de position dans les fonctions 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...

Dans check_args, $#s'étend au nombre d'arguments passés à la fonction elle-même, qui dans ce script est toujours 0.

Si vous voulez une telle fonctionnalité dans une fonction shell, vous devez écrire quelque chose comme ceci à la place:

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

Cela fonctionne car $#est développé en dehors de la fonction et transmis à la fonction comme l'un de ses paramètres de position. À l'intérieur de la fonction, se $1développe au premier paramètre positionnel qui a été passé à la fonction shell, plutôt qu'au script dont elle fait partie.

Ainsi, comme $#les paramètres spéciaux $1, $2etc., ainsi que $@et $*, se rapportent également aux arguments passés à une fonction, quand ils sont étendus dans la fonction. Cependant, $0ne pas changer le nom de la fonction, ce qui est la raison pour laquelle j'étais encore capable de l' utiliser pour produire un message d'erreur de qualité.

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

De même, si vous définissez une fonction à l'intérieur d'une autre, vous travaillez avec les paramètres de position passés à la fonction la plus interne dans laquelle l'expansion est effectuée:

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

J'ai appelé ce script nestedet (après avoir exécuté chmod +x nested) je l'ai exécuté:

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

Oui je sais. "1 arguments" est un bug de pluralisation.

Les paramètres de position peuvent également être modifiés.

Si vous écrivez un script, les paramètres positionnels en dehors d'une fonction seront les arguments de ligne de commande passés au script, sauf si vous les avez modifiés .

Une façon courante de les changer est avec la fonction shiftintégrée, qui décale chaque paramètre de position vers la gauche d'un, en supprimant le premier et en diminuant $#de 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".

Ils peuvent également être modifiés avec la fonction setintégrée:

#!/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
Bravo pour la réponse éducative qui va au-delà de ce qui a été demandé mais qui répond aux intentions d'apprentissage du demandeur d'origine et d'autres comme moi!
Ricardo
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.