Esta página puede ser redistribuida libremente bajo los términos de la licencia GPL. Vease ( GPL texto original ) o si lo prefiere (Traducción española no oficial de la GPL) Al margen de las obligaciones legales que se derivan del uso de esta licencia rogamos sea respetada la referencia a su lugar de publicación original www.ciberdroide.com. y a su autor original Antonio Castro Snurmacher (Madrid 01/01/2000). |
![]() |
Esta ausencia de garantía se hace extensa a cualquier tipo de uso de este material y muy especialmente a las prácticas, ejercicios, y de ejemplos que encuentre en estas páginas. Deberá trabajar siempre salvo indicación contraria con un SO Linux y con un usario distinto de 'root' sin privilegios especiales. Como directorio de trabajo se procurará usar el directorio '/tmp' o algún otro que no contenga información valiosa. Tampoco se considera buena idea practicar en una máquina que contenga información valiosa. Todo esto son recomendaciones de prudencia. En cualquier caso si algo sale mal toda la responsabilidad será únicamente suya. En ningún caso podrá reclamar a nadie por daños y perjuicios derivados del uso de este material. Para más información lea el contenido de la licencia GPL y abstengase de hacer prácticas si no está dispuesto a asumir toda la responsabilidad.
|
Funciones
Si tecleamos 'set' después de esto veremos todas las variables
definidas y también aparecerá esta función que acabamos de crear.
Ahora vamos a editar un shell-script que use una función. Edite
un fichero que llamaremos 'pru_funcion1' que tenga lo siguiente.
Ahora damos permisos de ejecución y ejecutamos simplemente mediante
'./pru_funcion1'.
Edite y pruebe el siguiente código.
Para definir la sintaxis de una función en shell-script podríamos
usar lo siguiente:
Códigos de retorno
Ahora edite un fichero que llamaremos 'pru_funcion2' que tenga lo siguiente.
Ejecutaremos este ejemplo
Comandos true y false
En una expresión lógica donde lo que interesa es simplemente distinguir
entre algo que se cumple o que no se cumple se tomará 0 como valor lógico
TRUE (o sea cierto) y distinto de 0 se tomará como FALSE (falso).
'true' y 'false' son comandos que no hacen nada. Simplemente retornan
el valor lógico TRUE o FALSE respectivamente. El valor de $? siempre
devuelve el último código de retorno obtenido por la shell después de
ejecutar el último comando.
Ejemplos:
Esperamos que ejemplos tan complicados como estos dos últimos no le
desanimen para continuar con el curso.
Comando test
'test' es un comando externo que devolverá un código de retorno 0 o
1 dependiendo del resultado de la expresión que siga a continuación.
Se usa muchísimo y tiene dos formatos. 'test <expr>' o '[ <expr> ]'.
ambas formas son equivalentes.
Las expresiones de este tipo que solo pueden valer TRUE o FALSE se
denominan expresiones lógicas.
Ejemplos:
Comando expr
A diferencia de 'bc' no podemos usar valores demasiado altos porque el
resultado no sería correcto. Recuerde que 'bc' trabaja con precisión
arbitraria.
El comando 'expr' es útil pero no deja de ser un comando externo y por lo
tanto su uso resulta ineficiente. Afortunadamente la propia shell tiene
capacidad de evaluar expresiones aritméticas.
Expresiones aritméticas dentro de Bash
Puede que el operador '%' no le resulte familiar. Es el operador resto
también se llama módulo y es lo que sobra después de la división. Lo
cierto es que es preferible continuar explicando otras cosas y más adelante
tendremos oportunidad de poner algunos ejemplos con expresiones aritméticas.
Hay una serie de operadores que trabajan a nivel de bits pero no vamos
a explicarlos de momento.
Operadores '&&' y '||'
'<comando1> && <comando2> && <comando3>' significa
que debe cumplirse <comando1> y <comando2> y <comando3>
para que se cumpla la expresión y
'<comando1> || <comando2> || <comando3>' significa que
debe cumplirse <comando1> o <comando2> o <comando3> para
que se cumpla la expresión.
Si el resultado de '<comando1> && <comando2> && <comando3>' fuera
0 (TRUE) significaría que los tres comandos han retornado 0 (TRUE).
Si por el contrario el resultado hubiera sido distinto (FALSE)
solo sabríamos que por lo menos uno de los comandos retornó FALSE.
Supongamos que el primer comando retornó TRUE. La shell deberá
continuar ejecutando el segundo comando. Supongamos que el segundo
comando retorna FALSE. En este momento la shell no continua ejecutando
el tercer comando porque da igual lo que retorne el resultado será
FALSE. La razón es que se necesitaban todos TRUE para un resultado TRUE.
El hecho de no ejecutar las partes siguientes una vez se sabe el
resultado es lo que se llama circuito de evaluación corto y su uso
principal es el de ahorrar ejecuciones de ordenes innecesarias aunque se
puede usar tambien para ejecutar cosas solo bajo ciertas condiciones.
Con el operador '||' (OR) pasa algo similar.
Si el resultado de '<comando1> || <comando2> || <comando3>' fuera
1 (FASE) significaría que los tres comandos han retornado 1 (FALSE).
Si por el contrario el resultado hubiera sido distinto 0 (TRUE)
solo sabríamos que por lo menos uno de los comandos retornó TRUE.
Supongamos que el primer comando retornó FALSE. La shell deberá
continuar ejecutando el segundo comando. Supongamos que el segundo
comando retorna TRUE. En este momento la shell no continua ejecutando
el tercer comando porque da igual lo que retorne el resultado será
TRUE. Se necesitaban todos FALSE para un resultado FALSE.
Un comando normal como por ejemplo 'grep' o 'echo' que termina bien
devuelve TRUE. Si hubiéramos redirigido la salida de 'grep'o de 'echo'
a un fichero sin permiso de escritura o a un fichero con un path
inexistente el retorno de 'grep' o de 'echo' habría sido FALSE indicando
la existencia de error. En otras ocasiones FALSE no indicará error
sino que el comando no tuvo éxito. Esto último sería aplicable a 'grep'
(patrón localizado o no localizado) pero no sería aplicable a un
comando como 'echo'.
Veamos un ejemplo con 'grep'.
Cuando ejecutamos comandos con un pipe ('|') el código de retorno
es el del último comando ejecutado. En nuestro caso
el primer código 1 se debe a la imposibilidad de generar la salida.
El segundo código 1 se debe a que la cadena no fue encontrada.
En los tres casos $? recoge el código retornado por 'grep'.
Ahora vamos a probar ejemplos que ilustren el circuito de evaluación
corto. Primero para OR
Ahora para AND.
El circuito de evaluación corto ha de usarse correctamente.
Si sabemos que un comando dará casi siempre un resultado que
puede hacer innecesario la ejecución de otros comandos lo
colocaremos en primer lugar. Los comandos lentos se colocan en
último lugar porque de esa forma puede no ser necesario ejecutarlos.
Algunas veces no nos interesa un resultado sino solo que se ejecute
una orden dependiendo del resultado de otra orden. Es decir queremos
ejecutar solo si se cumple una condición.
Test
Se pueden definir funciones que podrán ser llamadas por
su nombre permitiendo el paso de parámetros de la
misma forma que cuando se llama a una shell. También
puede devolver códigos de retorno. Las variables alteradas
dentro de una función tienen efecto visible fuera
de la función ya que una función no se ejecuta invocando
a una sub-shell. Una función se puede definir en un
shell-script o en la propia linea de comandos. Si se
hace esto último será como si añadiéramos un comando
interno.
$ fff() { echo "---------------------------" ; }
$ fff
---------------------------
$ set
# pru_funcion1
# Funcion que muestra dos parametros de entrada y modifica una variable
funcion1()
{
echo '<'$1'>'
echo '('$2')'
var=1234
}
## main ##
funcion1 111 222
echo $var
$ ./pru_funcion1
<111>
(222)
33
1234
# Visualizar los parametros del 6 al 14
function()
{
shift 5
echo $1 $2 $3 $4 $5 $6 $7 $8 $9
}
## main ##
funcion 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
_nombre_funcion_ () { _lista_de_ordenes_ }
Una lista de ordenes sería una o más ordenes separadas por algún
operador como por ejemplo ';', '||', '&&', '&' o
$ echo \
La palabra reservada 'return' interrumpe la ejecución de una función
asignando un valor al código de retorno de esa función. Su uso no
es necesario al final de la función. La palabra reservada 'exit'
termina la ejecución
de un shell-script liberando recursos y devolviendo un código de retorno
el intérprete de comandos.
'exit 0' terminará retornando cero. Si solo ponemos 'exit' el código
retornado será como si hiciéramos 'exit $?'. Es decir retorna el valor
del último comando.
# pru_funcion2
# Funcion que devuelve un código de retorno. Y shell que devuelve otro
funcion2()
{
var=1234
return 33
var=4567
}
## main ##
funcion2
echo $?
echo $var
exit 888
$./pru_funcion2
33
1234
Los comandos que finalizan correctamente suelen retornar 0. Los que
finalizan con algún error suelen retornar un valor distinto de 0 y de
esa forma distintos códigos de retorno pueden indicar distintas causas
de error o no éxito.
$ true ; echo $?
0
$ false ; echo $?
1
Como ya dijimos el lenguaje shell-script utiliza muy frecuentemente
comandos externos del sistema operativo. Queremos explicar el comando
'test' antes de explicar las sentencias condicionales de bash porque
de esta forma podremos usar ejemplos más completos.
-r <fichero>
TRUE si el fichero existe y es legible.
-w <fichero>
TRUE si el fichero existe y se puede escribir.
-x <fichero>
TRUE si el fichero existe y es ejecutable.
-f <fichero>
TRUE si el fichero existe y es de tipo regular (normal).
-d <fichero>
TRUE si existe y es un directorio.
-c <fichero>
TRUE si existe y es un dispositivo especial de caracteres.
-b <fichero>
TRUE si existe y es un dispositivo especial de bloques.
-p <fichero>
TRUE si existe y es un pipe (fifo).
-u <fichero>
TRUE si existe y tiene el bit set-user-ID.
-s <fichero>
TRUE si existe y tiene tamaño mayor que 0.
-z <cadena>
TRUE si es una cadena vacía.
-n <cadena>
TRUE si es una cadena no vacía.
<cadena1> = <cadena2>
TRUE si ambas cadenas son idénticas. OJO hay dejar espacios
a un lado y otro del signo igual para no confundir la orden
con una asignación.
<cadena1> != <cadena2>
TRUE si son distintas.
<cadena>
TRUE si no es cadena nula. (Una variable que no existe
devuelve cadena nula).
<num1> -eq <num2>
TRUE si ambos números son iguales.
<num1> -ne <num2>
TRUE si ambos números son distintos.
<num1> -gt <num2>
TRUE si <num1> mayor que <num2>.
<num1> -ge <num2>
TRUE si <num1> mayor o igual que <num2>.
<num1> -lt <num2>
TRUE si <num1> menor que <num2>.
<num1> -le <num2>
TRUE si <num1> menor o igual que <num2>.
! <expresión_logica>
Operador lógico NOT retorna TRUE si <expresión_logica> es
FALSE y retorna FALSE si <expresión_logica> es TRUE.
<ExprLogi1> -a <ExprLogi2>
Operador lógico AND retorna TRUE si <ExprLogi1> y
<ExprLogi2> son ambas TRUE y FALSE en caso contrario.
<ExprLogi1> -o <ExprLogi2>
Operador lógico OR retorna TRUE si <ExprLogi1> o
<ExprLogi2> son alguna de ellas TRUE y FALSE en caso contrario.
( <expr> )
Se pueden usar paréntesis para agrupar expresiones lógicas.
Es decir que valgan TRUE o FALSE
$ test -r /etc/passwd ; echo $?
0
$ test -w /etc/passwd ; echo $?
1
$ test -x /etc/passwd ; echo $?
1
$ test -c /dev/null ; echo $?
0
$ test -r /etc/passwd -a -c /dev/null ; echo $?
0
$ test -w /etc/passwd -a -c /dev/null ; echo $?
1
$ test -r /etc/passwd -a -f /dev/null ; echo $?
1
$ [ -s /dev/null ] ; echo $?
1
$ [ ! -s /dev/null ] ; echo $?
0
$ [ "$$" = "zzzzzzzzzzzz" ] ; echo $?
1
$ [ 0 -lt $$ ] ; echo $?
0
$ [ 0 -lt $$ -a true ] ; echo $?
Este comando admite como parámetros los elementos de una expresión
aritmética pero hay que recordar que algunos
operadores deben ser escapados con '\'. Por ejemplo .
$ expr 11 \* 2
La shell por defecto asume que todas las variables son de tipo
cadena de caracteres.
Para definir una variable de tipo numérico se usa 'typeset -i'.
Esto añade un atributo a esa variable para que la shell realice una
expansión distinta sobre esta variable.
Para evaluar una expresión aritmética asignándola a una variable
usaremos 'let'.
- +
Menos unário y Más unário
* / %
Multiplicación, División, y Resto
+ -
Suma, y Resta
<= >= < >
== !=
Comparaciones Menor o igual,
Mayor o igual, Menor, Mayor
Igualdad, desigualdad
= *= /= %= += -=
Asignación simple,
Asignación con operador
(multiplicación, división, resto, suma, y resta)
$ typeset -i j=7
$ typeset -i k
$ typeset -i m
$ echo $j
7
$ let j=j+3
$ echo $j
10
$ let j+=3
$ echo $j
13
$ let k=j%3
$ let m=j/3
$ echo '(' $m '* 3 ) +' $k '=' $j
+AMA
( 4 * 3 ) + 1 = 13
Son operadores AND y OR a nivel de shell y poseen circuito de evaluación
corto. No tienen que ver con los operadores -a y -o ya que
estos eran interpretados por el comando 'test'.
Estos operadores '&&' y '||' serán colocados separando comandos que
lógicamente retornan siempre algún valor.
$ echo hola | grep hola ; echo $?
hola
0
$ echo hola | grep hola > /hh/hh/hh ; echo $?
bash: /hh/hh/hh: No existe el fichero o el directorio
1
$ echo xxx | grep hola ; echo $?
1
$ # Como un OR queda evaluado cuando se encuentra el primer resultado TRUE
$ # solo se evaluaran los comandos hasta obtener el primero que devuelva
$ # 0 (TRUE)
$ echo "hola" || echo "adiós"
hola
$ # El resultado sera 0
$ echo $?
0
$ # Como un AND no queda hasta que se evalúa el primer resultado FALSE
$ # se evaluaran los dos comandos y devolverá igualmente TRUE.
$ echo "hola" && echo "adiós"
hola
adiós
$ # El resultado sera 0
$ echo $?
0
$ test ! -w fichero && echo "Cuidado fichero sin permiso de escritura"
$ test -w fichero || echo "Cuidado fichero sin permiso de escritura"
Puede comprobar sus conocimientos respondiendo el siguiente test.
Para ello seleccione las opciones que se ajusten a la verdad y luego
pulse el boton para ver el resultado de su test.
Si quiere hacernos llegar alguna duda, aclaración,
crítica, o contribución personal, utilice nuestro
formulario de contacto y nosotros le contestaremos