|
|
| Este artículo está disponible en los siguientes idiomas: English Castellano Deutsch Francais Nederlands Portugues Turkce |
by Katja and Guido Socher About the author: Katja es la editora alemana de LinuxFocus. Le gusta Tux, las películas & la fotografía y el mar. Puedes visitar su página personal aquí. Guido es un fan de Linux desde hace mucho tiempo y le gusta Linux porque está diseñado por personas abiertas y honestas. Esta es una de las razones por la que lo llamamos código abierto (open source). Su página personal está en linuxfocus.org/~guido. Content: |
Abstract:
En este artículo se explica cómo escribir pequeños scripts de shell utilizando muchos ejemplos.
#!/bin/sh
Lo caracteres #! indican al sistema que el primer argumento que
sigue en la linea es el programa a utilizar para ejecutar este fichero.
En este caso usamos el shell /bin/sh.nombre_de_la_variable=valorPara obtener el valor de la variable simplemente hay que poner el signo del dólar delante de la variable:
#!/bin/sh # asignamos un valor: a="hola mundo" # ahora mostramos el contenido de "a": echo "A es:" echo $aEscribe estas líneas en tu editor de textos y guardalo p.ej. como primero. Después haz ejecutable el script escribiendo chmod +x primero en el shell y después ejecútalo escribiendo ./primero
A es: hola mundoAlgunas veces es posible confundir nombres de variables con el resto del texto:
num=2 echo "este es el $numº"Aquí no se mostrará "este es el 2º" si no "este es el " porque el shell busca una variable llamada numdo que no tiene valor. Para decirle al shell que queremos mostrar la variable num tenemos que usar llaves:
num=2
echo "este es el ${num}º"
Con esto imprimiremos lo que queremos: este es el 2º | Sintaxis del comando | Utilidad |
|---|---|
| echo "texto cualquiera" | escribir un texto cualquiera en tu pantalla |
| ls | listar ficheros |
| wc -l fichero wc -w fichero wc -c fichero |
contar las líneas de un fichero o contar las palabras de un fichero o contar el número de carácteres |
| cp fichero_origen fichero_destino | copiar el fichero_origen al fichero_destino |
| mv nombre_antiguo nuevo_nombre | renombrar o mover un fichero |
| rm fichero | borrar un fichero |
| grep 'patrón' fichero | buscar cadenas en un fichero Ejemplo: grep 'cadena-a-buscar' fichero.txt |
| cut -b num_de_columna fichero | sacar datos de columnas de texto de ancho definido Ejemplo: sacar los caracteres de la posición 5 a la 9 cut -b5-9 fichero.txt No confundais este comando con "cat" que es algo totalmente diferente |
| cat fichero.txt | mostrar un fichero.txt por stdout (tu pantalla) |
| file un_fichero | describir qué tipo de fichero es un_fichero |
| read var | esperar a que el usuario introduzca algo y guardarlo en la variable (var) |
| sort fichero.txt | ordenar las líneas del fichero fichero.txt |
| uniq | borrar las líneas duplicadas, usado en combinación con sort
ya que uniq sólo borra las líneas duplicadas consecutivas Ejemplo: sort fichero.txt | uniq |
| expr | hacer operaciones matemáticas en el shell Ejemplo: sumar 2 y 3 expr 2 "+" 3 |
| find | buscar ficheros Ejemplo: buscar por nombre: find . -name nombre_del_fichero -print Este comando tiene muchas posibilidades y opciones diferentes. Desafortunadamente son demasiadas para explicarlas todas en este artículo. |
| tee | escribir datos por stdout (tu pantalla) y a un fichero Normalmente se usa así: un-comando | tee fichero-de-salida Escribe la salida de un-comando por la pantalla y a el fichero de salida |
| basename fichero | devolver el nombre del fichero dado un nombre y
quitarle la ruta al directorio Ejemplo: basename /bin/tux devuelve sólo tux |
| dirname fichero | devolver el nombre del directorio dado un nombre y
quitándole el nombre del fichero Ejemplo: dirname /bin/tux devuelve /bin |
| head fichero | mostrar algunas líneas del principio de un fichero |
| tail fichero | mostrar algunas líneas del final de un fichero |
| sed | sed es básicamente un programa para buscar y reemplazar. Lee el texto
de la entrada estándar (p.ej. una tubería) y escribe el resultado por
stdout (normalmente la pantalla). El patrón de búsqueda es una expresión
regular (ver referencias). Este patrón de búsqueda de se debe
confundir con la sintaxis de comodines (wildcard) del shell. Para reemplazar
la cadena linuxfocus con LinuxFocus es un fichero de texto usa: cat texto.fichero | sed 's/linuxfocus/LinuxFocus/' > nuevotexto.fichero Esto reemplaza la primera aparición de la cadena linuxfocus en cada línea por LinuxFocus. Si hay líneas en que linuxfocus aparece varias veces y quieres reemplazarlas todas usa: cat texto.fichero | sed 's/linuxfocus/LinuxFocus/g' > nuevotexto.fichero |
| awk |
La mayoría de las veces awk se utiliza para extraer campos de una
línea de un texto.
El campo separador por defecto es un espacio. Para especificar uno
diferente se usa la opción -F.
cat fichero.txt | awk -F, '{print $1 "," $3 }'
Aquí hemos usado la coma (,) como separador de campos e imprimimos
la columna primero y tercera ($1 $3). Si fichero.txt tiene líneas como:
Adam Bor, 34, India Kerry Miller, 22, USAestonces mostraría lo siguiente: Adam Bor, India Kerry Miller, USASe pueden hacer muchas más cosas con awk pero este es su uso más común. |
grep "hola" fichero.txt | wc -l
encuentra las líneas con la cadena hola en fichero.txt y después cuenta
el número de líneas.find . -mtime -1 -type f -printencuetra todos los ficheros que han sido modificados en las últimas 24 horas (-mtime -2 serían 48 horas). Si quisieramos empaquetar estos fichero en un archivo tar (fichero.tar) la sintaxis sería:
tar xvf fichero.tar fichero1 fichero2 ...En vez de escribirlo todo se pueden combinar los dos comandos (find y tar) usando comillas simples invertidas. Entonces tar empaquetará todos los ficheros que muestre find:
#!/bin/sh
# Las comillas son comillas simples invertidas (`) no comillas normales ('):
tar -zcvf ultimodific.tar.gz `find . -mtime -1 -type f -print`
if ....; then .... elif ....; then .... else .... fiLa mayoría de las veces se utiliza un comando muy especial dentro de las sentencias if. Se puede usar para comparar cadena o comprobar si un fichero existe, tiene permiso de lectura etc...
[ -f "un-fichero" ] : Comprueba si un-fichero es un fichero. [ -x "/bin/ls" ] : Comprueba si /bin/ls existe y es ejecutable. [ -n "$var" ] : Comprueba si la variable $var contiene algo [ "$a" = "$b" ] : Comprueba si las variables "$a" y "$b" son igualesEjecuta el comando "man test" y obtendrás una larga lista con todo tipo de operadores test para comparaciones y ficheros.
#!/bin/sh if [ "$SHELL" = "/bin/bash" ]; then echo "tu shell es el bash (bourne again shell)" else echo "tu shell no es bash sino $SHELL" fiLa variable $SHELL contiene el nombre de la shell donde estás y esto es lo que estamos comprobando aquí comparandola con la cadena "/bin/bash"
[ -f "/etc/shadow" ] && echo "Este ordenador usa shadow passwors"Los && se pueden usar como una pequeña sentencia if. Lo de la derecha se ejecuta si lo de la izquierda es verdadero. Podemos interpretarlo como un Y (AND). Por lo tanto el ejemplo quedaría: "El fichero /etc/shadow existe Y (AND) se ejecuta el comando echo". También está disponible el operador O (OR) (||). Un ejemplo:
#!/bin/sh
mailfolder=/var/spool/mail/james
[ -r "$mailfolder" ] || { echo "No se ha podido leer $mailfolder" ; exit 1; }
echo "$mailfolder tiene mensajes de:"
grep "^From " $mailfolder
El script comprueba primero si puede leer un buzón de correo. Si puede
entonces imprime las líneas "From" del buzón. Si no puede leer
el fichero $mailfolder entonces se activa el operador O. Sencillamente
este código lo leeríamos como "Mailfolder legible o salir del
programa". Aquí el problema es que debemos tener exactamente un
comando detrás de O pero en este caso necesitamos dos: case ... in ...) hacer algo aquí;; esacVeamos un ejemplo. El comando file comprueba que tipo de fichero es el fichero que le pasamos:
file lf.gz devuelve: lf.gz: gzip compressed data, deflated, original filename, last modified: Mon Aug 27 23:09:18 2001, os: UnixAhora vamos a usar esto para escribir un script llamado smartzip que puede descomprimir ficheros comprimidos con bzip2, gzip y zip automaticamente :
#!/bin/sh
tipofichero=`file "$1"`
case "$tipofichero" in
"$1: Zip archive"*)
unzip "$1" ;;
"$1: gzip compressed"*)
gunzip "$1" ;;
"$1: bzip2 compressed"*)
bunzip2 "$1" ;;
*) error "El fichero $1 no se puede descomprimir con smartzip";;
esac
select var in ... ; do break done .... ahora podemos usar $var ....Un ejemplo:
#!/bin/sh
echo "¿Cuál es tu sistema operativo favorito?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Otros"; do
break
done
echo "Has seleccionado $var"
Aquí tienes lo que haría el programa:
¿Cuál es tu sistema operativo favorito? 1) Linux 2) Gnu Hurd 3) Free BSD 4) Otros #? 1 Has seleccionado LinuxEn el shell tenemos están disponibles las siguientes sentencias de bucle:
while ...; do .... doneEl bucle while se ejecutará mientras la expresión que comprobamos sea verdadera. Se puede usar la palabra clave "break" para abandonar el bucle en cualquier punto de la ejecución. Con la palabra clave "continue" el bucle continua con la siguiente iteración y se salta el resto del cuerpo del bucle.
for var in ....; do .... doneEl siguente ejemplo muestra por pantalla de la letra A a la C:
#!/bin/sh for var in A B C ; do echo "var es $var" doneUn script de ejemplo más útil es este, llamado showrpm, que muestra un resumen del contenido de una serie de paquetes RPM:
#!/bin/sh
# listar un resumen del contenido de una serie de paquetes RPM
# USO: showrpm ficherorpm1 ficherorpm2 ...
# EJEMPLO: showrpm /cdrom/RedHat/RPMS/*.rpm
for rpmpackage in $*; do
if [ -r "$rpmpackage" ];then
echo "=============== $rpmpackage =============="
rpm -qi -p $rpmpackage
else
echo "ERROR: no se pudo leer el fichero $rpmpackage"
fi
done
Como se puede ver hemos usado la siguente variable especial, $* que
contiene todos los argumentos de la línea de comandos. Si ejecutas #!/bin/sh echo *.jpgImprimirá "correo.jpg tux.jpg".
#!/bin/sh echo "*.jpg" echo '*.jpg'Esto mostrará "*.jpg" dos veces.
#!/bin/sh echo $SHELL echo "$SHELL" echo '$SHELL'Esto mostrará:
/bin/bash /bin/bash $SHELLFinalmente existe la posibilidad de eliminar el significado especial de un único caracter anteponiendole una barra invertida:
echo \*.jpg echo \$SHELLEsto mostrará:
*.jpg $SHELLDocumentación aquí
#!/bin/sh
# tenemos menos de 3 argumentos. Mostramos el texto de ayuda:
if [ $# -lt 3 ] ; then
cat <<AYUDA
ren -- renombra varios ficheros usando expresiones regulares de sed
USO: ren 'regexp' 'reemplazo' ficheros...
EJEMPLO: rename all *.HTM fles in *.html:
ren 'HTM$' 'html' *.HTM
AYUDA
exit 0
fi
VIEJO="$1"
NUEVO="$2"
# El comando shift elimina un argumento de la lista
# de argumentos de la linea de comandos.
shift
shift
# ahora $* contiene todos los ficheros:
for fichero in $*; do
if [ -f "$fichero" ] ; then
nuevofichero=`echo "$fichero" | sed "s/${VIEJO}/${NUEVO}/g"`
if [ -f "$nuevofichero" ]; then
echo "ERROR: $nuevofichero ya existe"
else
echo "renombrando $fichero como $nuevofichero ..."
mv "$fichero" "$nuevofichero"
fi
fi
done
Hasta ahora este es el script más complejo que hemos visto. Hablemos sobre él
un poco. La primera sentencia if comprueba si hemos introducido al menos 3
parámetros de línea de comandos. (La variable especial $# contiene el
número de argumentos). Si no, se envia el texto de ayuda al comando
cat que consecuentemente lo muestra por pantalla. Después de mostrar el texto
de ayuda salimos del programa. Si hay 3 o más argumentos asignamos
el primer argumento a la variable VIEJO y el segundo a la
variable NUEVO. Lo siguiente es cambiar la posición de los parámetros de la lista de comandos
dos veces para poner el tercero en la primera posición de $*. Con $* entramos
en el bucle for. Cada uno de los argumentos de $* se asigna uno a uno
a la variable $fichero. Aquí comprobamos primero que el fichero
existe y después creamos un nuevo fichero usando buscar
y reemplazar con sed. Las comillas simples invertidas se usan para asignar el resultado
a la variable nuevofichero. Ya tenemos todo lo que necesitamos: El nombre de
fichero de viejo y el nuevo. Ahora sólo tenemos que utilizarlos con el comando
mv para renombrar los ficheros.
nombredelafuncion()
{
# dentro del cuerpo $1 es el primer argumento dado a la función
# $2 el segundo ...
cuerpo
}
Se necesita "declarar" las funciones al principio del script
antes de usarlas.
#!/bin/sh
# vim: set sw=4 ts=4 et:
ayuda()
{
cat <<AYUDA
xtitlebar -- cambiar el nombre de un xterm, gnome-terminal o konsole de kde
USO: xtitlebar [-h] "cadena_para_la_barra_de_titulo"
OPCIONES: -h texto de ayuda
EJEMPLO: xtitlebar "cvs"
AYUDA
exit 0
}
# en caso de error o si le pasamos -h llamar a la función ayuda:
[ -z "$1" ] && ayuda
[ "$1" = "-h" ] && ayuda
# enviamos la secuencia de escape para cambiar el título de la ventana:
echo -e "\033]0;$1\007"
#
Se recomienda siempre documentar detalladamente el código de los scripts.
Con esto posibilitamos que otras personas (y tú) puedan usar y entender
el script.
#!/bin/sh
ayuda()
{
cat <<AYUDA
Esta es la demostración de un analizador de línea de comandos genérico.
EJEMPLO DE USO: cmdparser -l hola -f -- -fichero1 fichero2
AYUDA
exit 0
}
while [ -n "$1" ]; do
case $1 in
-h) ayuda;shift 1;; # llamamos a la función ayuda
-f) opt_f=1;shift 1;; # la variable opt_f existe
-l) opt_l=$2;shift 2;; # -l toma un argumento -> cambiado 2 veces
--) shift;break;; # end of options
-*) echo "error: no existe la opción $1. -h para ayuda";exit 1;;
*) break;;
esac
done
echo "opt_f es $opt_f"
echo "opt_l es $opt_l"
echo "el primer argumento es $1"
echo "el 2º argumento es $2"
¡Pruébalo! Puedes ejecutarlo con p.ej.:
cmdparser -l hola -f -- -fichero1 fichero2Produce
opt_f es 1 opt_l es hola el primer argumento es -fichero1 el 2º argumento es fichero2¿Cómo funciona? Basicamente se realiza un bucle a través de todos los argumentos y los compara con la sentencia case. Si encuentra alguna coincidencia establece una variable y cambia la línea de comandos en uno. Por convención en unix las opciones (lo que tiene delante un signo menos) deben estar primero. Puedes indicar que han finalizado las opciones escribiendo dos signos menos (--). Lo necesitarás p.ej. con grep para buscar una cadena que comience con el signo menos:
Buscar la cadena -xx- en el fichero f.txt: grep -- -xx- f.txtNuestro analizador de opciones también puede manejar los -- como se puede comprobar en el listado superior.
cp framework.sh miscripty después insertar las funcionalidades necesarias en "miscript".
#!/bin/sh
# vim: set sw=4 ts=4 et:
ayuda()
{
cat <<AYUDA
b2d -- conversor binario a decimal
USO: b2d [-h] numeroenbinario
OPCIONES: -h texto de ayuda
EJEMPLO: b2d 111010
devolverá 58
AYUDA
exit 0
}
error()
{
# mostrar un error y salir
echo "$1"
exit 1
}
ultimocarac()
{
# devolver el último caracter de una cadena en $rval
if [ -z "$1" ]; then
# cadena vacía
rval=""
return
fi
# wc deja espacio antes de su salida y por eso necesitamos usar sed:
numdcarac=`echo -n "$1" | wc -c | sed 's/ //g' `
# ahora cortamos el último caracter
rval=`echo -n "$1" | cut -b $numdcarac`
}
chop()
{
# borrar el último caracter de la cadena y retornarno como $rval
if [ -z "$1" ]; then
# cadena vacía
rval=""
return
fi
# wc deja espacio antes de su salida y por eso necesitamos usar sed:
numdcarac=`echo -n "$1" | wc -c | sed 's/ //g' `
if [ "$numdcarac" = "1" ]; then
# sólo hay un carácter en la cadena
rval=""
return
fi
numdcaracmenos1=`expr $numdcarac "-" 1`
# ahora cortamos todos excepto el último:
rval=`echo -n "$1" | cut -b 0-${numdcaracmenos1}`
}
while [ -n "$1" ]; do
case $1 in
-h) ayuda;shift 1;; # llamamos a la función ayuda
--) shift;break;; # fin de las opciones
-*) error "error: no existe la opción $1. -h para ayuda";;
*) break;;
esac
done
# El programa principal
suma=0
peso=1
# hay que pasar un argumento:
[ -z "$1" ] && ayuda
numbin="$1"
numbinorig="$1"
while [ -n "$numbin" ]; do
ultimocarac "$numbin"
if [ "$rval" = "1" ]; then
suma=`expr "$peso" "+" "$suma"`
fi
# borrar la última posición en $numbin
chop "$numbin"
numbin="$rval"
peso=`expr "$peso" "*" 2`
done
echo "el binario $numbinorig en decimal es $suma"
#
El algoritmo usado en este script toma el peso decimal
(1,2,4,8,16,..) de cada dígito comenzando por el dígito más a la derecha
y lo añade a la suma si el dígito es un 1. De este modo "10" es:
#!/bin/sh
# vim: set sw=4 ts=4 et:
ver="0.1"
ayuda()
{
cat <<AYUDA
rotatefile -- rotar el nombre de un fichero
USO: rotatefile [-h] nombre_del_fichero
OPCIONES: -h texto de ayuda
EJEMPLO: rotatefile algo
Esto p. ej. renombrará algo.2 a algo.3, algo.1 a algo.2, algo a algo.1
y creará un fichero vacío llamado algo.
El número máximo es 10
version $ver
AYUDA
exit 0
}
error()
{
echo "$1"
exit 1
}
while [ -n "$1" ]; do
case $1 in
-h) ayuda;shift 1;;
--) break;;
-*) echo "error: no existe la opción $1. -h para ayuda";exit 1;;
*) break;;
esac
done
# comprobamos la entrada:
if [ -z "$1" ] ; then
error "ERROR: debe especificar un fichero, use -h para obtener ayuda"
fi
nomfich="$1"
# renombrar cualquier fichero .1 , .2 etc:
for n in 9 8 7 6 5 4 3 2 1; do
if [ -f "$nomfich.$n" ]; then
p=`expr $n + 1`
echo "mv $nomfich.$n $nomfich.$p"
mv $nomfich.$n $nomfich.$p
fi
done
# renombrar el fichero original:
if [ -f "$nomfich" ]; then
echo "mv $nomfich $nomfich.1"
mv $nomfich $nomfich.1
fi
echo touch $nomfich
touch $nomfich
¿Cómo funciona el programa? Después de comprobar que el usuario ha proporcionado un
nombre de fichero entramos en un bucle for desde 9 a 1. El fichero 9 se
renombra como 10, el 8 como 9 así con todos. Después del bucle renombramos el
fichero original como 1 y creamos un fichero vacío con el nombre del
fichero original.
sh -x scriptconerrorEsto ejecutará el script y mostrará todas la sentencias que se ejecutan con las variables y comodines ya expandidos.
sh -n tu_scriptSi no retorna nada entonces tu programa no tiene errores de sintaxis.
|
|
Webpages maintained by the LinuxFocus Editor team
© Katja and Guido Socher, FDL LinuxFocus.org Click here to report a fault or send a comment to LinuxFocus |
Translation information:
|
2001-10-26, generated by lfparser version 2.17