Pasar parámetros a una función Bash


Answers

El conocimiento de lenguajes de programación de alto nivel (C / C ++ / Java / PHP / Python / Perl ...) sugeriría al profano que las funciones de bash deberían funcionar como lo hacen en esos otros lenguajes. En cambio , las funciones bash funcionan como comandos de shell y esperan que los argumentos se les pasen de la misma manera que se podría pasar una opción a un comando de shell (ls -l). En efecto, los argumentos de función en bash se tratan como parámetros posicionales ( $1, $2..$9, ${10}, ${11} , etc.). Esto no es sorprendente teniendo en cuenta cómo funciona getopts . Los paréntesis no están obligados a llamar a una función en bash.

( Nota : estoy trabajando en Open Solaris por el momento).

# bash style declaration for all you PHP/JavaScript junkies. :-)
# $1 is the directory to archive
# $2 is the name of the tar and zipped file when all is done.
function backupWebRoot () {
    tar -cvf - $1 | zip -n .jpg:.gif:.png $2 - 2>> $errorlog &&
        echo -e "\nTarball created!\n"
}


# sh style declaration for the purist in you. ;-)
# $1 is the directory to archive
# $2 is the name of the tar and zipped file when all is done.
backupWebRoot () {
    tar -cvf - $1 | zip -n .jpg:.gif:.png $2 - 2>> $errorlog &&
        echo -e "\nTarball created!\n"
}


#In the actual shell script
#$0               $1            $2

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

Intento buscar cómo pasar parámetros en una función de Bash, pero lo que surge siempre es cómo pasar el parámetro desde la línea de comando.

Me gustaría pasar parámetros dentro de mi script. Lo intenté:

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

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

Pero la sintaxis no es correcta, ¿cómo pasar un parámetro a mi función?




Pensé en conectarme con la mención de otra forma de pasar los parámetros nombrados a bash ... pasando por referencia. Esto es compatible desde bash 4.0

#!/bin/bash
function myBackupFunction(){ # directory options destination filename
local directory="$1" options="$2" destination="$3" filename="$4";
  echo "tar cz ${!options} ${!directory} | ssh root@backupserver \"cat > /mnt/${!destination}/${!filename}.tgz\"";
}

declare -A backup=([directory]=".." [options]="..." [destination]="backups" [filename]="backup" );

myBackupFunction backup[directory] backup[options] backup[destination] backup[filename];

Una sintaxis alternativa para bash 4.3 es usar un nameref

A pesar de que nameref es mucho más conveniente en el sentido de que elimina las referencias de forma directa, algunas distribuciones más antiguas todavía incluyen una versión anterior, por lo que aún no la recomendaré.




Si prefiere parámetros con nombre, es posible (con algunos trucos) pasar parámetros con nombre a funciones (también permite pasar matrices y referencias).

El método que desarrollé te permite definir parámetros con nombre pasados ​​a una función como esta:

function example { args : string firstName , string lastName , integer age } {
  echo "My name is ${firstName} ${lastName} and I am ${age} years old."
}

También puede anotar argumentos como @required o @readonly, crear ... argumentos en reposo, crear matrices a partir de argumentos secuenciales (usando, por ejemplo, la string[4] ) y opcionalmente enumerar los argumentos en múltiples líneas:

function example {
  args
    : @required string firstName
    : string lastName
    : integer age
    : string[] ...favoriteHobbies

  echo "My name is ${firstName} ${lastName} and I am ${age} years old."
  echo "My favorite hobbies include: ${favoriteHobbies[*]}"
}

En otras palabras, no solo puede llamar sus parámetros por sus nombres (lo que representa un núcleo más legible), sino que puede pasar matrices (y referencias a variables; ¡esta característica solo funciona en bash 4.3)! Además, las variables asignadas están todas en el ámbito local, al igual que $ 1 (y otros).

El código que hace este trabajo es bastante ligero y funciona tanto en bash 3 como en bash 4 (estas son las únicas versiones con las que lo he probado). Si te interesan más trucos como este que hacen que el desarrollo con bash sea mucho más agradable y fácil, puedes echar un vistazo a mi Bash Infinity Framework , el siguiente código está disponible como una de sus funcionalidades.

shopt -s expand_aliases

function 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 # first colon ":" or next parameter's comma ","
    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
      string | boolean) declaration="local " ;;
      integer) declaration="local -i" ;;
      reference) declaration="local -n" ;;
      arrayDeclaration) declaration="local -a"; wrapLeft= ; wrapRight= ;;
      assocDeclaration) declaration="local -A"; wrapLeft= ; wrapRight= ;;
      "string["*"]") declaration="local -a"; length="${nextType//[a-z\[\]]}" ;;
      "integer["*"]") 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 \"Parameter '$nextName' ($nextType) is marked as required by '${FUNCNAME[1]}' function.\"; " >&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 "Unexpected arguments after a rest array ($nextName) in '${FUNCNAME[1]}' function." >&2
        fi
      elif [[ "$length" -gt 1 ]]
      then
        paramRange="{@:$paramIndex:$length}"
        paramIndex+=$((length - 1))
      fi

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

      # continue to the next param:
      shift
    fi
  done
  echo "${evalString} local -i __paramIndex=${paramIndex};"
}

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





Tags