1404 votes

Transmettre des paramètres à une fonction Bash

Je suis en train d'essayer de chercher comment passer des paramètres dans une fonction Bash, mais ce qui s'affiche est toujours comment passer des paramètres à partir de la ligne de commande.

Je voudrais passer des paramètres à l'intérieur de mon script. J'ai essayé :

myBackupFunction("..", "...", "xx")

function myBackupFunction($directory, $options, $rootPassword) {
     ...
}

Mais la syntaxe n'est pas correcte. Comment puis-je passer un paramètre à ma fonction ?

12 votes

"... mais ce qui se passe toujours, c'est comment passer des paramètres depuis la ligne de commande" - Oui! C'est parce que les scripts Bash sont essentiellement des séquences de lignes de commande - invoquez une fonction dans un script Bash exactement comme s'il s'agissait d'une commande sur la ligne de commande! :-) Votre appel serait ma fonctionDeSauvegarde ".." "..." "xx"; aucun parenthèse, pas de virgules.

4 votes

Le pendant de cette question: renvoyer une valeur d'une fonction bash

0 votes

2165voto

dogbane Points 85749

Il y a deux façons typiques de déclarer une fonction. Je préfère la deuxième approche.

function function_name {
   commande...
} 

ou

function_name () {
   commande...
} 

Pour appeler une fonction avec des arguments:

function_name "$arg1" "$arg2"

La fonction fait référence aux arguments passés par leur position (et non par leur nom), c'est-à-dire $1, $2, et ainsi de suite. $0 est le nom du script lui-même.

Exemple:

function_name () {
   echo "Le paramètre #1 est $1"
}

De plus, vous devez appeler votre fonction après l'avoir déclarée.

#!/usr/bin/env sh

foo 1  # cela échouera car foo n'a pas encore été déclarée.

foo() {
    echo "Le paramètre #1 est $1"
}

foo 2 # cela fonctionnera.

Sortie:

./myScript.sh: ligne 2: foo: commande introuvable
Le paramètre #1 est 2

Référence: Guide avancé de scripting Bash.

2 votes

Quand j'écris function name(){}, j'obtiens une erreur concernant '('. Mais quand j'écris name(){}, cela fonctionne. Des idées ?

5 votes

Vous avez oublié les espaces, essayez function name() {}. Peut-être avec un 'entrée' avant {}

32 votes

Bonne réponse. Mes 2 cents: dans les constructions shell qui résident dans un fichier qui est sourcé (pointé) quand nécessaire, je préfère utiliser le mot-clé function et le (). Mon objectif (dans un fichier, pas une ligne de commande) est d'augmenter la clarté, pas de réduire le nombre de caractères tapés, à savoir, function myBackupFunction() compound-statement.

160voto

Anthony Rutledge Points 280

La connaissance des langages de programmation de haut niveau (C/C++, Java, PHP, Python, Perl, etc.) suggérerait à un profane que les fonctions Shell Bourne Again (Bash) fonctionnent comme celles des autres langages.

Cependant, les fonctions Bash fonctionnent comme des commandes shell et attendent que les arguments leur soient passés de la même manière qu'on pourrait passer une option à une commande shell (par exemple, ls -l). En effet, les arguments de fonction en Bash sont traités comme des paramètres positionnels ($1, $2..$9, ${10}, ${11}, et ainsi de suite). Ce n'est pas surprenant étant donné le fonctionnement de getopts. Ne pas utiliser de parenthèses pour appeler une fonction en Bash.


(Remarque : Je suis en train de travailler sur OpenSolaris en ce moment.)

# Déclaration de style Bash pour tous les fans de PHP/JavaScript. :-)
# $1 est le répertoire à archiver
# $2 est le nom de l'archive tar et zippée une fois tout est terminé.
function backupWebRoot ()
{
    tar -cvf - "$1" | zip -n .jpg:.gif:.png "$2" - 2>> $errorlog &&
        echo -e "\nArchive créée !\n"
}

# Déclaration de style sh pour les puristes en vous. ;-)
# $1 est le répertoire à archiver
# $2 est le nom de l'archive tar et zippée une fois tout est terminé.
backupWebRoot ()
{
    tar -cvf - "$1" | zip -n .jpg:.gif:.png "$2" - 2>> $errorlog &&
        echo -e "\nArchive créée !\n"
}

# Dans le script shell réel
# $0               $1            $2

backupWebRoot ~/public/www/ webSite.tar.zip

Vous voulez utiliser des noms pour les variables ? Il suffit de faire quelque chose de similaire à ceci.

local filename=$1 # Le mot-clé declare peut être utilisé, mais local est sémantiquement plus spécifique.

Soyez prudent, cependant. Si un argument d'une fonction contient un espace, vous voudrez peut-être faire cela à la place ! Sinon, $1 pourrait ne pas être ce que vous pensez.

local filename="$1" # Juste pour être du bon côté. Bien que, si $1 était un entier, alors quoi ? Est-ce même possible? Humm.

Vous voulez passer un tableau à une fonction par valeur ?

callingSomeFunction "${someArray[@]}" # Développe tous les éléments du tableau.

A l'intérieur de la fonction, manipulez les arguments de cette manière.

function callingSomeFunction ()
{
    for value in "$@"; do # Vous voulez utiliser "$@" ici, pas "$*" !!!!!
        :
    done
}

Vous devez passer une valeur et un tableau, mais utiliser quand même "$@" à l'intérieur de la fonction ?

function linearSearch ()
{
    local myVar="$1"

    shift 1 # Supprime $1 de la liste des paramètres

    for value in "$@"; do # Représente les paramètres restants.
        if [[ $value == $myVar ]]; then
            echo -e "Trouvé !\t... après un certain temps."
            return 0
        fi
    done

    return 1
}

linearSearch $someStringValue "${someArray[@]}"

À partir de Bash 4.3 et au-dessus, vous pouvez passer un tableau à une fonction par référence en définissant le paramètre d'une fonction avec l'option -n.

function callingSomeFunction ()
{
    local -n someArray=$1 # aussi ${1:?} pour rendre le paramètre obligatoire.

    for value in "${someArray[@]}"; do # Super!
        :
    done
}

callingSomeFunction myArray # Pas de $ devant l'argument. Vous passez par nom, pas par expansion/valeur.

0 votes

Le dernier exemple publié ne fonctionne pas autant que je sache. J'ai essayé de l'exécuter sur bash v5+ et il me renvoie simplement le tableau complet dans la boucle au lieu de chaque élément.

1 votes

Après avoir testé à nouveau, j'ai trouvé que c'était mon erreur car je déclarais le tableau en ligne au lieu de le déclarer avant.

3 votes

@iomv Néanmoins, faites attention au problème de "référence de variable circulaire". Peu importe le nom que vous donnez au tableau dans la fonction, NE NOMMEZ PAS votre argument de tableau dans le contexte d'appel / code client du même nom. Remarquez comment j'ai changé le dernier exemple pour aider les gens à éviter le problème de "référence de nom circulaire". Bonne décision, même si vous avez commis une erreur vous-même. :-)

83voto

NIXin Points 44

Si vous préférez les paramètres nommés, il est possible (avec quelques astuces) de passer en fait des paramètres nommés aux fonctions (ce qui permet également de passer des tableaux et des références).

La méthode que j'ai développée vous permet de définir des paramètres nommés passés à une fonction comme ceci :

fonction exemple { args : chaîne prénom , chaîne nom de famille , entier âge } {
  echo "Mon nom est ${prénom} ${nom de famille} et j'ai ${âge} ans."
}

Vous pouvez également annoter les arguments comme @required ou @readonly, créer des arguments ...rest, créer des tableaux à partir d'arguments séquentiels (en utilisant par exemple chaîne[4]) et éventuellement lister les arguments sur plusieurs lignes :

fonction exemple {
  args
    : @required chaîne prénom
    : chaîne nom de famille
    : entier âge
    : chaîne[] ...hobbiesPréférés

  echo "Mon nom est ${prénom} ${nom de famille} et j'ai ${âge} ans."
  echo "Mes hobbies préférés incluent : ${hobbiesPréférés[*]}"
}

En d'autres termes, non seulement vous pouvez appeler vos paramètres par leurs noms (ce qui permet une écriture plus lisible), mais vous pouvez aussi passer des tableaux (et des références à des variables - cette fonctionnalité ne fonctionne qu'avec Bash 4.3 cependant) ! De plus, les variables mappées se trouvent toutes dans la portée locale, tout comme $1 (et autres).

Le code qui permet cela est assez léger et fonctionne à la fois sous Bash 3 et Bash 4 (ce sont les seules versions avec lesquelles je l'ai testé). Si vous êtes intéressé par d'autres astuces de ce type qui rendent le développement avec Bash plus agréable et plus facile, vous pouvez jeter un œil à mon Framework Bash Infinity, le code ci-dessous est disponible comme l'une de ses fonctionnalités.

shopt -s expand_aliases

fonction assignTrap {
  local evalString
  local -i paramIndex=${__paramIndex-0}
  local initialCommand="${1-}"

  if [[ "$initialCommand" != ":" ]]
  then
    echo "trap - DEBUG; eval \"${__previousTrap}\"; unset __previousTrap; unset __paramIndex;"
    return
  fi

  while [[ "${1-}" == "," || "${1-}" == "${initialCommand}" ]] || [[ "${#@}" -gt 0 && "$paramIndex" -eq 0 ]]
  do
    shift # Premier deux-points ":" ou virgule de paramètre suivante ","
    paramIndex+=1
    local -a decorators=()
    while [[ "${1-}" == "@"* ]]
    do
      decorators+=( "$1" )
      shift
    done

    local declaration=
    local wrapLeft='"'
    local wrapRight='"'
    local nextType="$1"
    local length=1

    case ${nextType} in
      chaîne | booléen) declaration="local " ;;
      entier) declaration="local -i" ;;
      référence) declaration="local -n" ;;
      déclarationTableau) declaration="local -a"; wrapLeft= ; wrapRight= ;;
      déclarationAssoc) declaration="local -A"; wrapLeft= ; wrapRight= ;;
      "chaîne["*"]") declaration="local -a"; length="${nextType//[a-z\[\]]}" ;;
      "entier["*"]") declaration="local -ai"; length="${nextType//[a-z\[\]]}" ;;
    esac

    if [[ "${declaration}" != "" ]]
    then
      shift
      local nextName="$1"

      for decorator in "${decorators[@]}"
      do
        case ${decorator} in
          @readonly) declaration+="r" ;;
          @required) evalString+="[[ ! -z \$${paramIndex} ]] || echo \"Le paramètre '$nextName' ($nextType) est marqué comme requis par la fonction '${FUNCNAME[1]}'.\"; " >&2 ;;
          @global) declaration+="g" ;;
        esac
      done

      local paramRange="$paramIndex"

      if [[ -z "$length" ]]
      then
        # ...rest
        paramRange="{@:$paramIndex}"
        # trim leading ...
        nextName="${nextName//\./}"
        if [[ "${#@}" -gt 1 ]]
        then
          echo "Arguments inattendus après un tableau rest ($nextName) dans la fonction '${FUNCNAME[1]}'." >&2
        fi
      elif [[ "$length" -gt 1 ]]
      then
        paramRange="{@:$paramIndex:$length}"
        paramIndex+=$((length - 1))
      fi

      evalString+="${declaration} ${nextName}=${wrapLeft}\$${paramRange}${wrapRight}; "

      # Continuer avec le prochain paramètre :
      shift
    fi
  done
  echo "${evalString} local -i __paramIndex=${paramIndex};"
}

alias args='local __previousTrap=$(trap -p DEBUG); trap "eval \"\$(assignTrap \$BASH_COMMAND)\";" DEBUG;'

0 votes

Quels sont les variables @var, @reference, @params ? Que dois-je chercher sur Internet pour en apprendre davantage à ce sujet ?

1 votes

Bonjour @niieani lorsque j'essaie de créer une fonction bash sous la forme que vous utilisez dans votre réponse, il me dit que je dois installer ucommon utils depuis apt. Est-ce comme cela que fonctionne votre script bash? Est-ce que je le fais correctement? Si je comprends bien, vous ou quelqu'un d'autre avez essentiellement créé le programme util ucommon pour permettre une extension de Bash, n'est-ce pas?

0 votes

@DavidA.French non, cela ne devrait pas se produire. Il n'y a pas de relation entre ucommon et mon code. Il est possible que vous ayez installé un outil qui cause le problème que vous avez mentionné, aucune idée de ce que cela pourrait être.

50voto

nbt Points 15965

Lâchez les parenthèses et les virgules :

 myBackupFunction ".." "..." "xx"

Et la fonction devrait ressembler à ceci :

function myBackupFunction() {
    # Ici $1 est le premier paramètre, $2 le deuxième, etc.
}

19voto

Adiii Points 5246

Un exemple simple qui va effacer à la fois lors de l'exécution du script ou à l'intérieur du script lors de l'appel d'une fonction.

#!/bin/bash
echo "exemple de fonction avec paramètres"
function print_param_value(){
    value1="${1}" # $1 représente le premier argument
    value2="${2}" # $2 représente le deuxième argument
    echo "paramètre 1 est  ${value1}" # Comme chaîne de caractères
    echo "paramètre 2 est  ${value2}"
    sum=$(($value1+$value2)) # Les traiter comme des nombres
    echo "La somme des deux valeurs est  ${sum}"
}
print_param_value "6" "4" # Valeurs séparées par un espace
# Vous pouvez également passer des paramètres lors de l'exécution du script
print_param_value "$1" "$2" # Paramètres $1 et $2 lors de l'exécution

# Supposons que le nom de notre script soit "param_example".
# Appelez-le comme ceci:
#
# ./param_example 5 5
#
# Maintenant les paramètres seront $1=5 et $2=5

0 votes

Pour ce que ça vaut, je suis également ce formulaire mais je me demande s'il y a un avantage supplémentaire ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X