|
|
Este documento 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/shLo 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 -lencuentra 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 " $mailfolderEl 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 doneComo 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 doneHasta 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