Programación Shell-Script PDF
Programación Shell-Script PDF
Subsecciones
Ejecución de un script
Paso de parámetros
Entrada/salida
Redirecciones
Tests
Estructura if...then...else
Comando test
Expresiones
Chequeo de strings
Chequeo de enteros
Chequeo de ficheros
Operadores lógicos con test
Comando de test extendido
Control de flujo
Estructura case
Lazos for
Bucle while
Bucle until
break y continue
Funciones
Paso de parámetros
return
Otros comandos
wait
trap
exit
Referencias indirectas
Optimización de scripts
Depuración
Programación Shell-Script
Bash (y otros shells) permiten programar scripts:
Script o programa shell:
fichero de texto conteniendo comandos externos e internos, que se ejecutan línea por línea
www.ac.usc.es/docencia/ASRI/Tema_3html/node34.html
Ejecución de un script
Los scripts deben empezar por el número mágico #! seguido del programa a usar para interpretar
el script:
#!/bin/bash script de bash
#!/bin/sh script de shell
#!/usr/bin/perl script de perl
ejecutar una shell poniendo como argumento el nombre del script (sólo necesita permiso de lectura)
$ bash helloworld
o bien:
$ source helloworld
Paso de parámetros
$ cat parms1.sh
#!/bin/bash
VAL=$((${1:-0} + ${2:-0} + ${3:-0}))
echo $VAL
$ bash parms1.sh 2 3 5
10
$ bash parms1.sh 2 3
5
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 2/15
25/6/2020 Programación Shell-Script
El comando shift desplaza los parámetros hacia la izquierda el número de posiciones indicado:
$ cat parms2.sh
#!/bin/bash
echo $#
echo $*
echo "$1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11}"
shift 9
echo $1 $2 $3
echo $#
echo $*
$ bash parms2.sh a b c d e f g h i j k l
12
a b c d e f g h i j k l
a b c d e f g h i j k
j k l
3
j k l
Entrada/salida
Es posible leer desde la entrada estándar o desde fichero usando read y redirecciones:
#!/bin/bash
echo -n "Introduce algo: "
read x
echo "Has escrito $x"
echo -n "Escribe 2 palabras: "
read x y
echo "Primera palabra $x; Segunda palabra $y"
#!/bin/bash
# FILE: linelist
# Usar: linelist filein fileout
# Lee el fichero pasado en filein y
# lo salva en fileout con las lineas numeradas
count=0
while read BUFFER
do
count=$((++count))
echo "$count $BUFFER"» $2
done < $1
#!/bin/bash
# Usa $IFS para dividir la línea que se está leyendo
# por defecto, la separación es "espacio"
echo "Lista de todos los usuarios:"
OIFS=$IFS # Salva el valor de IFS
IFS=: # /etc/passwd usa ":"para separar los campos
cat /etc/passwd |
while read name passwd uid gid fullname ignore
do
echo "$name ($fullname)"
done
IFS=$OIFS # Recupera el $IFS original
Redirecciones
if true
then
read x
read y
fi < fichero1
#/bin/bash
read buf
while [ "$buf" ]
do
echo $buf
read buf
done | tr 'a-z' 'A-Z' > tmp.$$
Tests
Los comandos que se ejecutan en un shell tienen un código de salida, que se almacena en la
variable $?
si $? es 0 el comando terminó bien
si $? es > 0 el comando terminó mal
Ejemplo:
$ ls /bin/ls
/bin/ls
$ echo $?
0
$ ls /bin/ll
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 4/15
25/6/2020 Programación Shell-Script
Podemos chequear la salida de dos comandos mediante los operadores && (AND) y || (OR)
estos operadores actúan en cortocircuito:
Estructura if...then...else
Podemos usar el estado de salida de uno o varios comandos para tomar decisiones:
if comando1
then
ejecuta otros comandos
elif comando2
then
ejecuta otros comandos
else
ejecuta otros comandos
fi
Ejemplo:
$ cat if.sh
#!/bin/bash
if (ls /bin/ls && ls /bin/ll) >/dev/null 2>&1
then
echo "Encontrados ls y ll"
else
echo "Falta uno de los ficheros"
fi
$ bash if.sh
Falta uno de los ficheros
Comando test
Notar que if sólo chequea el código de salida de un comando, no puede usarse para comparar
valores: para eso se usa el comando test
El comando test permite:
chequear la longitud de un string
comparar dos strings o dos números
chequear el tipo de un fichero
chequear los permisos de un fichero
combinar condiciones juntas
test expresión
o bien
[ expresión ]3
en el segundo if la expresión es correcta si $2 tiene algún valor; falsa si la variable no está definida o
contiene null ("")
Expresiones
Chequeo de strings
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 6/15
25/6/2020 Programación Shell-Script
Expresión Verdadero sí
string el string es no nulo ("")
-z string la longitud del string es 0
-n string la longitud del string no es 0
string1 = string2 los strings son iguales
string1 != string2 los strings son distintos
Chequeo de enteros
Expresión Verdadero sí
int1 -eq int2 los enteros son iguales
int1 -ne int2 los enteros son distintos
int1 -gt int2 int1 mayor que int2
int1 -ge int2 int1 mayor o igual que int2
int1 -lt int2 int1 menor que int2
int1 -le int2 int1 menor o igual que int2
Chequeo de ficheros
Expresión Verdadero sí
-e file file existe
-r file file existe y es legible
-w file file existe y se puede escribir
-x file file existe y es ejecutable
-f file file existe y es de tipo regular
-d file file existe y es un directorio
-c file file existe y es un dispositivo de caracteres
-b file file existe y es un dispositivo de bloques
-p file file existe y es un pipe
-S file file existe y es un socket
-L file file existe y es un enlace simbólico
-u file file existe y es setuid
-g file file existe y es setgid
-k file file existe y tiene activo el sticky bit
-s file file existe y tiene tamaño mayor que 0
Expresión Propósito
! invierte el resultado de una expresión
-a operador AND
-o operador OR
( expr ) agrupación de expresiones; los paréntesis tienen un significado especial para el shell, por lo que
hay que escaparlos
Ejemplos:
$ test -f /bin/ls -a -f /bin/ll ; echo $?
1
$ test -c /dev/null ; echo $?
0
$ [ -s /dev/null ] ; echo $?
1
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 7/15
25/6/2020 Programación Shell-Script
Ejemplos:
$ [[ -f /bin/ls && -f /bin/ll ]] ; echo $?
1
$ [[ $$ -gt 0 && ($$ -lt 5000 || -w file) ]]
Control de flujo
Además del if bash permite otras estructuras de control de flujo: case, for, while y until
Estructura case
case valor in
patrón_1)
comandos si value = patrón_1
comandos si value = patrón_1 ;;
patrón_2)
comandos si value = patrón_2 ;;
*)
comandos por defecto ;;
esac
si valor no coincide con ningún patrón se ejecutan los comandos después del *)
esta entrada es opcional
patrón puede incluir comodines y usar el símbolo | como operador OR
Ejemplo:
#!/bin/bash
echo -n "Respuesta:" read RESPUESTA
case $RESPUESTA in
S* | s*)
RESPUESTA="SI";;
N* | n*)
RESPUESTA="NO ";;
*)
RESPUESTA="PUEDE";;
esac
echo $RESPUESTA
Lazos for
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 8/15
25/6/2020 Programación Shell-Script
LISTA="10 9 8 7 6 5 4 3 2 1"
for var in $LISTA
do
echo $var
done
dir="/var/tmp"
for file in $dir/*.bak
do
rm -f $file
done
LIMIT=10
for ((a=1, b=LIMIT; a <= LIMIT; a++, b--))
do
echo "$a-$b"
done
Bucle while
while comando
do
comandos
done
Ejemplo:
while [ $1 ]
do
echo $1
shift
done
Bucle until
until comando
do
comandos
done
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 9/15
25/6/2020 Programación Shell-Script
Ejemplo:
until [ "$1" = ""]
do
echo $1
shift
done
break y continue
Funciones
y para llamarla:
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 10/15
25/6/2020 Programación Shell-Script
funcion p1 p2 p3
Paso de parámetros
La función referencia los parámetros pasados por posición, es decir, $1, $2, ..., y $* para la lista
completa:
$ cat funcion1.sh
#!/bin/bash
funcion1()
{
echo "Parámetros pasados a la función: $*"
echo "Parámetro 1: $1"
echo "Parámetro 2: $2"
}
# Programa principal
funcion1 "hola" "que tal estás" adios
$
$ bash funcion1.sh
Parámetros pasados a la función: hola que tal estás adios
Parámetro 1: hola
Parámetro 2: que tal estás
return
Después de llamar a una función, $? tiene el código se salida del último comando ejecutado:
podemos ponerlo de forma explícita usando return
#!/bin/bash
funcion2() {
if [ -f /bin/ls -a -f /bin/ln ]; then
return 0
else
return 1
fi
}
# Programa principal
if funcion2; then
echo "Los dos ficheros existen"
else
echo "Falta uno de los ficheros - adiós"
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 11/15
25/6/2020 Programación Shell-Script
exit 1
fi
Otros comandos
wait
trap
$ cat trap.sh
#!/bin/bash
cachado() {
echo "Me has matado!!!"
kill -15 $$
}
trap "cachado" 2 3
while true; do
true
done
$ bash trap.sh
(Ctrl-C)
Me has matado!!!
Terminado
3 quit
9 kill (no puede ser parada ni ignorada)
15 terminate; señal por defecto generada por kill
exit
Finaliza el script
se le puede dar un argumento numérico que toma como estado de salida, p.e. exit 0 si el script acaba
bien y exit 1 en caso contrario
si no se usa exit, el estado de salida del script es el del último comando ejecutado
Referencias indirectas
a=letra
letra=z
# Referencia directa
echo "a = $a" # a = letra
# Referencia indirecta
eval a=\$$a
echo "Ahora a = $a" # Ahora a = z
Las versiones de bash a partir de la 2 permiten una forma más simple para las referencias
indirectas:
a=letra
letra=z
# Referencia directa
echo "a = $a" # a = letra
# Referencia indirecta
echo "Ahora a = ${!a}" # Ahora a = z
Optimización de scripts
do
count=$(expr $count + 1)
done < $1
echo "El fichero $1 tiene $count líneas"
$ cat cuentalineas2.sh
#!/bin/bash
count=0
while read line
do
count=$(($count+1))
done < $1
echo "El fichero $1 tiene $count líneas"
el tiempo ahora
$ time bash cuentalineas2.sh Quijote.txt
El fichero Quijote.txt tiene 36855 líneas
real 0m1.014s
user 0m0.887s
sys 0m0.108s
Y todavía mejor:
$ cat cuentalineas3.sh
#!/bin/bash
count=$(wc -l $1 | cut -d " " -f 1)
echo "El fichero $1 tiene $count líneas"
$
$ time bash cuentalineas3.sh Quijote.txt
El fichero Quijote.txt tiene 36855 líneas
real 0m0.096s
user 0m0.005s
sys 0m0.009s
Conclusiones
Intenta reducir el número de procesos creados al ejecutar el script, por ejemplo, usando las
funciones aritméticas del shell
Siempre que sea posible, intenta usar comandos del shell (wc, tr, grep, sed, etc.) en vez de lazos
Depuración
$ cat cuentalineas3.sh
#!/bin/bash
set -x
count=$(wc -l $1 | cut -d " "-f 1)
set +x
echo "El fichero $1 tiene $count líneas"
$
$ bash cuentalineas3.sh Quijote.txt
++ wc -l Quijote.txt
++ cut -d ' '-f 1
+ count=36855
+ set +x
El fichero Quijote.txt tiene 36855 líneas
Curso de Administración de Sistemas y Redes por Tomás Fernández Pena se distribuye bajo la licencia
Creative Commons Recoñecemento-Compartir baixo a mesma licenza. 3.0 España.
Trabajo original en persoal.citius.usc.es/tf.pena/ASR.
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 15/15