0% encontró este documento útil (0 votos)
115 vistas15 páginas

Programación Shell-Script PDF

Este documento describe la programación de scripts en Bash y otros shells. Explica cómo ejecutar scripts, pasar parámetros, realizar entrada/salida, usar redirecciones, estructuras condicionales como if/then/else, bucles como for y while, funciones, y otros comandos como wait y trap. Proporciona ejemplos detallados de cada concepto para ilustrar su uso en scripts de shell.

Cargado por

David Galvez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
115 vistas15 páginas

Programación Shell-Script PDF

Este documento describe la programación de scripts en Bash y otros shells. Explica cómo ejecutar scripts, pasar parámetros, realizar entrada/salida, usar redirecciones, estructuras condicionales como if/then/else, bucles como for y while, funciones, y otros comandos como wait y trap. Proporciona ejemplos detallados de cada concepto para ilustrar su uso en scripts de shell.

Cargado por

David Galvez
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 15

25/6/2020 Programación Shell-Script

Siguiente: Manejo de ficheros de Subir: Programación de scripts de Anterior: Programación de scripts de


Índice de Materias

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

El programa puede contener, además de comandos


1. variables
2. constructores lógicos (if...then, AND, OR, etc.) y lazos (while, for, etc.)
3. funciones
4. comentarios

Para saber más:


persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html Ú 1/15
25/6/2020 Programación Shell-Script

Advanced Bash-Scripting Guide, Mendel Cooper, Última revisión Mayo 2005,


www.tldp.org/guides.html
The Deep, Dark Secrets of Bash, Ben Okopnik, Linux Gazette,
okopnik.freeshell.org/articles/Shell_Scripting-4.html
Introduction to Shell Scripting, Ben Okopnik, okopnik.freeshell.org/writings.html

Más detalles en:

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

Las forma usuales de ejecutar un script es:


darle permiso de ejecución al fichero y ejecutarlo como un comando:
$ chmod +x helloworld
./helloworld

ejecutar una shell poniendo como argumento el nombre del script (sólo necesita permiso de lectura)
$ bash helloworld

ejecutarlo en la shell actual


$ . helloworld

o bien:
$ source helloworld

Paso de parámetros

Es posible pasar parámetros a un scripts: los parámetros se recogen en las variables $1 a $9


Variable Uso
$0 el nombre del script
$1 a $9 parámetros del 1 al 9
${10}, ${11},... parámetros por encima del 10
$# número de parámetros
$*, $@ todos los parámetros
Ejemplo:

$ 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"

Si queremos leer o escribir a un fichero utilizamos redirecciones:

echo $X > fichero


read X < fichero

Este último caso lee la primera línea de fichero y la guarda en la variable X


Si queremos leer un fichero línea a línea podemos usar while:

#!/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

el fichero de entrada se va leyendo línea a línea y almacenando en BUFFER


count cuenta las líneas que se van leyendo
El uso de lazos para leer ficheros es bastante ineficiente
deberían evitarse (por ejemplo, usar cat fichero)

Ejemplo de lectura de fichero


persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 3/15
25/6/2020 Programación Shell-Script

#!/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

El fichero /etc/passwd se lee línea a línea


para cada línea, sus campos se almacenan en las variables que siguen a read
la separación entre campos la determina la variable $IFS (por defecto, espacio en blanco)

Redirecciones

Las redirecciones y pipes pueden usarse en otras estructuras de control

Ejemplo: lee las 2 primeras líneas de un fichero

if true
then
read x
read y
fi < fichero1

Ejemplo: lee líneas de teclado y guardalas en un fichero temporal convirtiendo minúsculas en


mayúsculas

#/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

ls: /bin/ll: Non hai tal ficheiro ou directorio


$ echo $?
1

Podemos chequear la salida de dos comandos mediante los operadores && (AND) y || (OR)
estos operadores actúan en cortocircuito:

comando1 && comando2


comando2 sólo se ejecuta si comando1 acaba bien
comando1 || comando2
comando2 sólo se ejecuta si comando1 falla

comandos true y false: devuelven 0 y 1, respectivamente

Ejemplo con &&:

$ ls /bin/ls && ls /bin/ll


/bin/ls
ls: /bin/ll: Non hai tal ficheiro ou directorio
$ echo $?
1
$ ls /bin/ll && ls /bin/ls
ls: /bin/ll: Non hai tal ficheiro ou directorio
$ echo $?
1

Ejemplo con ||:


$ ls /bin/ls || ls /bin/ll
/bin/ls
$ echo $?
0
$ ls /bin/ll || ls /bin/ls
ls: /bin/ll: Non hai tal ficheiro ou directorio
/bin/ls
$ echo $?
0

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

debe respetarse la colocación de los then, else y fi


también puede escribirse if comando1 ; then
el elif y el else son opcionales, no así el fi
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 5/15
25/6/2020 Programación Shell-Script

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 puede usarse de dos formas:

test expresión

o bien

[ expresión ]3

Si la expresión es correcta test devuelve un código de salida 0, si es falsa, devuelve 1:


este código puede usarse para tomar decisiones:
if [ "$1" = "hola" ]
then
echo "Hola a ti también"
else
echo "No te digo hola"
fi
if [ $2 ]
then
echo "El segundo parámetro es $2"
else
echo "No hay segundo parámetro"
fi

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

Existen expresiones para chequear strings, números o ficheros

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

Operadores lógicos con test

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

$ [ ! -w /etc/passwd ] && echo "No puedo escribir"


No puedo escribir
$ [ $$ -gt 0 -a \( $$ -lt 5000 -o -w file \) ]

Comando de test extendido

A partir de la versión 2.02 de Bash se introduce el extended test command: [[ expr ]]


permite realizar comparaciones de un modo similar al de lenguajes estándar:
permite usar los operadores && y || para unir expresiones
no necesita escapar los paréntesis

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

for var in lista


do
comandos
done

var toma los valores de la lista


puede usarse globbing para recorrer los ficheros

Ejemplo: recorrer una lista

LISTA="10 9 8 7 6 5 4 3 2 1"
for var in $LISTA
do
echo $var
done

Ejemplo: recorrer los ficheros *.bak de un directorio

dir="/var/tmp"
for file in $dir/*.bak
do
rm -f $file
done

Sintaxis alternativa, similar a la de C

LIMIT=10
for ((a=1, b=LIMIT; a <= LIMIT; a++, b--))
do
echo "$a-$b"
done

Bucle while

while comando
do
comandos
done

se ejecuta mientras que el código de salida de comando sea cierto

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

se ejecuta hasta que el código de salida de comando sea hace cierto

Ejemplo:
until [ "$1" = ""]
do
echo $1
shift
done

break y continue

Permiten salir de un lazo (break) o saltar a la siguiente iteración (continue)


break permite especificar el número de lazos de los que queremos salir (break n)

Ejemplo con break:


# Imprime el contenido de los ficheros hasta que
# encuentra una línea en blanco
for file in $*
do
while read buf
do
if [ -z "$buf"]
then
break 2
fi
echo $buf
done < $file
done

Ejemplo con continue:


# Muestra un fichero pero no las líneas de más
# de 80 caracteres
while read buf
do
cuenta=`echo $buf | wc -c`
if [ $cuenta -gt 80 ]
then
continue
fi
echo $buf
done < $1

Funciones

Podemos definir funciones en un script de shell:


funcion() {
comandos
}

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

Siempre tenemos que definir la función antes de llamarla:


#!/bin/bash
# Definición de funciones
funcion1() {
comandos
}
funcion2() {
comandos
}
# Programa principal
funcion1 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

Permite esperar a que un proceso lanzado en background termine


sort $largefile > $newfile &
ejecuta comandos
wait
usa $newfile

Si lanzamos varios procesos en background podemos usar $!


$! devuelve el PID del último proceso lanzado

sort $largefile1 > $newfile1 &


SortPID1=$!
sort $largefile2 > $newfile2 &
SortPID2=$!
ejecuta comandos
wait $SortPID1
usa $newfile1
wait $SortPID2
usa $newfile2

trap

Permite atrapar las señales del sistema operativo


permite hacer que el programa termine limpiamente (p.e. borrando ficheros temporales, etc.) aún en el
evento de un error

$ 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

Las señales más comunes para usar con trap son:


Señal Significado
0 salida del shell (por cualquier razón, incluido fin de fichero)
1 colgar
2 interrupción (Ctrl-C)
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 12/15
25/6/2020 Programación Shell-Script

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

Permiten definir variables cuyo contenido es el nombre de otra variable:

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

Otro ejemplo con eval


$ cat dni.sh
#!/bin/bash
dniPepe=23456789
dniPaco=98765431
echo -n "Nombre: "; read nombre
eval echo "DNI = \$dni${nombre}"
$ bash dni.sh
Nombre: Pepe
DNI = 23456789

Optimización de scripts

El shell no es especialmente eficiente a la hora de ejecutar trabajos pesados


Ejemplo: script que cuenta las líneas de un fichero:
$ cat cuentalineas1.sh
#!/bin/bash
count=0
while read line
persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 13/15
25/6/2020 Programación Shell-Script

do
count=$(expr $count + 1)
done < $1
echo "El fichero $1 tiene $count líneas"

si medimos el tiempo que tarda

$ time bash cuentalineas1.sh Quijote.txt


El fichero Quijote.txt tiene 36855 líneas
real 0m59.757s
user 0m17.868s
sys 0m41.462s

Podemos mejorarlo si usamos aritmética de shell en vez de el comando expr

$ 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

Para depurar un script de shell podemos usar la opción -x o -o xtrace de bash:


muestra en la salida estándar trazas de cada comando y sus argumentos, después de que el comando se
haya expandido pero antes de que se sea ejecutado

$ bash -x cuentalineas3.sh Quijote.txt


++ wc -l Quijote.txt
++ cut -d ' ' -f 1
+ count=36855
+ echo 'El fichero Quijote.txt tiene 36855 líneas'
El fichero Quijote.txt tiene 36855 líneas

Es posible depurar sólo parte de un script:


persoal.citius.usc.es/tf.pena/ASR/Tema_2html/node20.html 14/15
25/6/2020 Programación Shell-Script

poner set -x o set -xv al inicio del trozo a depurar


set +x o set +xv para cancelar

$ 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

Siguiente: Manejo de ficheros de Subir: Programación de scripts de Anterior: Programación de scripts de


Índice de Materias

Administración de Sistemas e Redes <ASR.USC[at]gmail.com>


Tomás Fernández Pena <tf.pena[at]usc.es>
Última actualización: 30-09-15 17:44 por tomas

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

También podría gustarte