3.3. Parámetros y variables

Como en cualquier lenguaje de programación, en el lenguaje shell se pueden crear y utilizar variables, que aquí se llaman parámetros. Existen varios tipos de parámetros:

  1. Si el nombre es un número se denominan parámetros posicionales.

  2. Si el nombre es un carácter especial se denominan parámetros especiales.

  3. El resto se denominan simplemente variables.

A continuación se detallan cada uno de estos tipos. Vea el apartado 2.5 del estándar para obtener más información.

3.3.1. Variables

El shell permite realizar las siguientes operaciones básicas con las variables:

Sólo definición
VAR=""
VAR=
Definición y/o inicialización/modificación
VAR=valor
Expansión (acceso a valor)
$VAR
${VAR}
Eliminación de la variable
unset VAR

Sobre ello deben realizarse las siguientes observaciones respecto a:

  1. Definición y uso:

    • Las variables sólo existen en el proceso shell en que son definidas (locales al proceso).

    • Las variables sólo son accesibles desde el momento de su definición hacia abajo del script, esto es, siempre deben definirse primero e invocarse después (no puede usarse una variable que es definida más adelante). Pueden utilizarse también dentro de funciones (ver más adelante) aunque se hayan declarado fuera de ellas. En el estándar POSIX todas las variables son globales, aunque existen variantes (como bash) que permiten la creación de variables locales a las funciones.

    No es necesario definir las variables previamente a su uso, ya que se crean al asignarles la primera vez un valor. Al definir una variable sin inicialización, su valor por omisión es la cadena nula, esto es, las siguientes entradas son equivalentes:

    VAR=
    
    VAR=""
    

    Con el comando set (sin argumentos) puede ver todas las variables (y funciones) definidas en el shell actual.

  2. Nombrado: téngase en cuenta que Linux es «case sensitive» (sensible a mayúsculas y minúsculas) en general, lo que incluye el nombre de las variable. Así, VAR y var serán tomadas como dos variables independientes . Para el nombre de una variable puede usarse :

    • 1er carácter: una letra o el carácter de subrayado _.

    • 2º y posteriores caracteres: una letra, dígito o el carácter de subrayado.

  3. Inicialización/Modificación del valor de una variable:

    • Es importante no incluir ningún espacio ni antes ni después del signo =. Si se hace, el shell intentará interpretar el nombre de la variable como un comando (recuerde que la sintaxis del shell es especialmente estricta en lo que a espacios se refiere).

    • El valor de una variable siempre es tomado por el shell como una cadena de caracteres.

    • Si el valor de una variable contiene caracteres especiales, espacios, u otros elementos que puedan ser malinterpretados por el shell, tendrá que ir entrecomillado o deberá incluir el carácter de escape donde sea necesario.

  4. Expansión de una variable: el uso de las llaves {} sólo es necesario si justo tras el nombre de la variable se desean escribir más caracteres, sin añadir ningún espacio antes. Se verá más adelante con más detalle.

Tareas

Mire el contenido del script script_variables.sh, que deberá contener lo siguiente:

script_variables.sh
   echo "Mal definida":
   echo "(orden no encontrada)":
   VAR = 1
   echo "Variables bien definidas:"
   VAR=1
   VAR1=2
   var=3
   echo "Variables: $VAR $VAR1 $var"
   echo "Variable VAR: $VAR"
   echo "Variable VAR1: $VAR1"
   echo "VAR seguida de 1: ${VAR}1"
   echo "Comillas dobles: $VAR"
   echo 'Comillas simples: $VAR'
   echo "Valor: $VAR-1"

Compruebe que dispone del permiso de ejecución general. Invóquelo y analice su funcionamiento.

3.3.1.1. Variables del shell

Existe un conjunto de variables que afectan al funcionamiento del shell. Muchas ya han sido analizadas en temas anteriores, por ejemplo: HOME, PATH, LANG,… Puede volver a ver una descripción de ellas en el apartado 2.5.3 del estándar.

Aquí vamos a destacar la variable IFS (Input Field Separators). El valor de esta variable es una lista de caracteres que se emplearán en el proceso de división de campos realizado tras el proceso de expansión (que se verá más adelante, en el apartado Expansiones y sustituciones) y por el comando read (ver el apartado Comandos internos). El valor por defecto es <espacio><tab><nueva-línea>. Podrá ver un ejemplo de uso de esta variable cuando realice los ejercicios propuestos (ver solución a script_user.sh).

3.3.2. Parámetros posicionales

Son los parámetros de la línea de comandos con la que se ha invocado al script (equivalen a la variable argv de C). Están denotados por un número y para obtener su valor se emplea $X o ${X} para los parámetros del 1 al 9 y ${X} para parámetros mayores (números de más de un dígito). Se pueden modificar con los comando set (los crea) y shift (los desplaza de posición).

3.3.3. Parámetros especiales

Son parámetros identificados por un carácter especial creados por el shell y cuyo valor no puede ser modificado directamente. A continuación se muestran los parámetros especiales definidos en el estándar:

$*

Se expande a todos los parámetros posicionales desde el 1. Si se usa dentro de comillas dobles, se expande como una única palabra formada por los parámetros posicionales separados por el primer carácter de la variable IFS (si la variable IFS no está definida, se usa el espacio como separador y si está definida a la cadena nula, los campos se concatenan).

$@

Se expande a todos los parámetros posicionales desde el 1, como campos separados, incluso aunque se use dentro de comillas dobles.

$0

Nombre del shell o shell-script que se está ejecutando.

$- (guion)

Opciones actuales del shell (modificables con el comando set). Consulte las opciones disponibles con el comando man dash .

$#

Nº de argumentos pasados al script (no incluye el nombre del script).

$?

Valor devuelto por el último comando, script, función o sentencia de control invocado. Recuerde que, en general, cualquier comando devuelve un valor. Usualmente, cuando un comando encuentra un error devuelve un valor distinto de cero.

$$

PID del proceso shell que está interpretando el script.

$!

PID del último proceso puesto en segundo plano.

Tareas

Mire el contenido del script script_var-shell.sh, que deberá contener lo siguiente:

script_var-shell.sh
   #!/bin/sh
   echo \$@=$@
   echo \$*=$*
   echo \$0=$0
   echo \$1=$1
   echo \$2=$2
   echo Cambio parametros posicionales
   set uno dos tres
   echo \$1=$1
   echo \$2=$2
   echo Desplazo
   shift
   echo \$1=$1
   echo \$2=$2
   echo \$-=$-
   echo \$#=$#
   echo \$?=$?
   firefox &
   ps w
   echo \$$=$$
   echo \$!=$!

Compruebe que dispone del permiso de ejecución. Invóquelo el comando y analice la salida:

./script_var-shell.sh arg1 arg2

3.3.4. Exportación de variables

Cuando un proceso (proceso padre, como por ejemplo el shell) ejecuta otro proceso (proceso hijo, otro programa o script), el proceso padre, además de los parámetros habituales (argc y argv en C), le pasa un conjunto de variables de entorno al proceso hijo (cada lenguaje de programación tiene su método propio para obtenerlas y modificarlas). Las variables de entorno pasadas pueden ser utilizadas por el proceso hijo para modificar su comportamiento. Por ejemplo, un programa en C puede usar la función getenv(3) declarada en biblioteca estándar stdlib.h para obtener dichas variables (puede obtener más información ejecutando man getenv).

El comando interno del shell export permite que una variable (previamente definida o no) sea configurada para que su valor sea copiado a los procesos hijos que sean creados desde el shell actual (por ejemplo otros shell). Presenta la sintaxis:

export VAR

export VAR=valor

En este último caso, sí es posible añadir un espacio antes o después del signo =.

Debe advertirse que «exportación» significa «paso de parámetros por valor», esto es, en el proceso hijo se creará una variable de igual nombre que en el shell padre, y con igual valor, pero serán variables independientes (esto es, la modificación de valor de la variable en el proceso hijo no afectará al valor de la variable en el shell padre). El proceso hijo no puede crear ni modificar variables del proceso padre.

Tareas

Mire el contenido de los siguientes scripts en su sistema:

script_padre.sh
#!/bin/bash
export VAR=a
echo $VAR
ps w
./script_hijo.sh
echo $VAR
script_hijo.sh
#!/bin/sh
echo $VAR
VAR=b
echo $VAR
ps w

Compruebe que dispone del permiso de ejecución. Ejecute el comando:

./script_padre.sh

Analice el resultado.

No debe confundirse la exportación con la invocación de shell-scripts mediante el mecanismo implícito basado en .. En este caso no hay ninguna copia de variables por valor, simplemente el script invocado es interpretado por el mismo shell.

Tareas

Mire el contenido de los siguientes scripts en su sistema:

script1.sh
#!/bin/bash
VAR=a
echo $VAR
ps w
. script2.sh
echo $VAR
script2.sh
#!/bin/sh
echo $VAR
VAR=b
echo $VAR
ps w

Compruebe que dispone del permiso de ejecución general. Ejecute el siguiente commando y analice el resultado:

./script1.sh

En los S.O.”s Linux suele ser habitual encontrar scripts que se dedican exclusivamente a contener la inicialización de un conjunto de variables, o la definición de un conjunto de funciones. Otros scripts del sistema hacen uso del mecanismo de invocación implícito basado en ., para cargar o importar las variables o funciones definidas en dichos scripts.

cont_func.sh
#!/bin/sh
fun1(){...}
fun2(){...}
#...
cont_var.sh
#!/bin/sh
VARa=1
VARb=2
#...
script_sistema.sh
#!/bin/sh
. /dir/cont_var.sh
. /dir2/cont_fun.sh
fun1 $VARb
#...

Tareas

Por ejemplo, visualice el contenido del script del sistema encargado del arranque de los servicios /etc/init.d/rc. Observe cómo contiene las líneas:

.  /etc/default/rcS
.  /lib/lsb/init-functions

Visualice el contenido de esos archivos rcS e init-functions y compruebe cómo sólo contienen definiciones de variables y funciones (la sintaxis para la definición de las funciones se analizará más adelante), respectivamente. De hecho, todos los ficheros de la carpeta /etc/default/ son scripts dedicados exclusivamente a contener la inicialización de variables, siendo importadas desde otros scripts del sistema. Por ejemplo, observe cómo:

  • La variable VERBOSE es inicializada en rcS y luego es usada por rc.

  • La función log_failure_msg() es definida en init-functions y luego es usada por rc.