Showing posts with label Programación. Show all posts
Showing posts with label Programación. Show all posts

11/04/2008


Programación en Bash

Exelente manual para la programación en bash, desde xtech.com.ar

Introducción

Linux, así como la mayoría de los UNIX, utilizan shell scripts para realizar una infinidad de tareas. Un shell script es un programa que se escribe con una sintaxis particular, en un archivo de texto plano, para que sea interpretado por un shell, en este caso /bin/bash.

Un shell script es básicamente un programa que llama a otros programas, con la posibilidad de hacer algun tipo de procesamiento propio (como control de flujo, operaciones matemáticas simples, etc).

Por lo tanto la forma de crear un shell script es hacer un archivo con nuestro ya querido vi:

vi holam

Luego lo llenamos con las instrucciones que deseamos. Por ejemplo:

#!/bin/bash
echo "Hola mundo!"
# Esto es por costumbre utilizado como ejemplo del primer programa.
# Se dice que trae suerte!

Luego le damos permisos de ejecución a ese archivo:

chmod +x holam

Y de esta manera ya tendremos listo un shell script que se ejecuta de la siguiente forma:

./holam

Debemos destacar la primera linea de nuestro script bash:

 #!/bin/bash

esta línea le indica al sistema que el script será interpretado por el programa que se encuentra a continuación de #!, en este caso, /bin/bash

Las últimas dos líneas son comentarios

 # Esto es por costumbre utilizado como ejemplo del primer programa.
# Se dice que trae suerte!

Los comentarios comienzan con # y se extienden hasta el final de la línea. Es muy útil ir comentando el código que uno escribe, para recordar qué realizan ciertas funciones o algoritmos, y otra persona pueda comprender el funcionamiento de nuestro script.


Variables

Las variables en un script BASH son simplemente identificadores, sin tipo. Para asignar un valor a una variable, se utiliza el operador =, por ejemplo:

[jose@xtech experto]$ MIVARIABLE=4

Por convención, los nombres de las variables se usan en mayúsculas, aunque no es obligatorio.

Para usar el contenido de la variable, dentro de un script, se usa el operador $. Por ejemplo:

[jose@xtech experto]$ echo $MIVARIABLE
4
[jose@xtech experto]$

Para utilizar el contenido de una variable, seguida de un texto, debemos usar las llaves {}

Consideremos este ejemplo:

 ARCHIVO="/tmp/ej"
mv $ARCHIVO $ARCHIVO-bak

En este caso, bash interpretaría a $ARCHIVO y $ARCHIVO-bak como dos variables distintas, para evitar esto debemos reescribirlo de esta manera:

 ARCHIVO="/tmp/ej"
mv $ARCHIVO ${ARCHIVO}-bak

Comillas

En el shell, el espacio, o el tab, son separadores. Es decir, que cuando al shell le indicamos

ls -l hola que tal

Lo interpreta como que le pedimos que nos de información sobre tres archivos, llamados: hola, que, y tal.

Si en realidad, lo que queríamos, era información sobre un archivo llamado "hola que tal", entonces hay varias maneras de indicarle al shell que los espacios entre esas palabras no deben ser separadores.

Escape ( \ )

Hay un caracter de escape, que indica al shell que el siguiente carácter no es especial. Y es la barra invertida. Por lo tanto, podríamos obtener la información del archivo "hola que tal" de la siguiente forma:

ls -l hola\ que\ tal

Los espacios no son especiales, no son separadores, y "hola que tal" es una sola palabra.

Algunos caracteres especiales más:

\ @ ! | < > [ ] { } ( ) ? * $ ' ^ ` " # & ;

Por lo tanto, si queremos incluir la \ en alguna parte, entonces debemos ponerla 2 veces (\\), la primera para decirle a BASH que no tome como carácter especial lo que sigue y la segunda como ese carácter que queremos incluír. Esto es muy común en casos como:

cd algún\ directorio\ con\ espacios

Comillas dobles ( " " )

Las comillas dobles hacen que los espacios entre las comillas no sean especiales. Por lo tanto, podríamos haber utilizado:

ls -l "hola que tal"

Todos los otros carácteres siguen siendo especiales.

Comillas simples ( ' ' )

Las comillas simples logran que ningún caracter (salvo la comilla simple misma) sea especial. Por ejemplo, si quisieramos crear un archivo que se llame *@$&, lo debemos hacer rodeándolo de comillas simples:

touch '*@$&'

Si queremos poner una comilla simple, debemos "escaparla". Para crear un archivo llamado que'tal, deberíamos hacerlo así:

touch 'que\'tal'

Ya que si no lo hacemos, la segunda comilla "cierra" la primera!

Comilla invertida ( ` ` )

Las comillas invertidas son más raras. Deben rodear un comando. Ese comando será ejecutado, y lo que ese comando imprima reemplazará al contenido de las comillas invertidas. Tal vez lo más sencillo sea un ejemplo:

[jose@xtech experto]$ ls
experto.aux experto.log experto.lyx experto.pdf experto.tex
experto.dvi #experto.lyx# experto.lyx~ experto.ps experto.toc

[jose@xtech experto]$ V=`ls`
[jose@xtech experto]$ echo $V
experto.aux experto.log experto.lyx experto.pdf experto.tex
experto.dvi #experto.lyx# experto.lyx~ experto.ps experto.toc
[jose@xtech experto]$

El uso más frecuente de las comillas invertidas es poder asignar el "resultado" de un comando a una variable.

Control de flujo

Esto refiere al cauce o flujo normal de los programas. No siempre hay un solo camino a seguir y estas proposiciones permiten que el programa realice distintas tareas, según las condiciones de las variables a interpretar.

"if"

El"if" es un proposición de control que verifica si es verdadera o falsa una condición.

Sintaxis:

if comando-condición
then
Comandos si la condición es verdadera
else
Comandos si la condición es falsa
fi

Es importante que la ubicación de las lineas se mantenga, ya que las proposiciones if/then/else/fi se controlan línea por línea. La parte else es opcional. En caso de querer hacer todo el control en una sola línea, las proposiciones deberán estar separadas por un punto y coma (;).

Los comandos que estén debajo del then se ejecutarán si la condición dio verdadera. Esto quiere decir que el valor de retorno de la condición fue 0 (cero). En caso contrario se ejecutarán los comandos que le siguen al else, si es que se utilizó esta proposición.

En la condición se puede poner un comando, donde la ejecución del mismo (su valor de retorno) definirá qué comandos (de qué proposición) se ejecutarán.

Si se verifica una condición, se deberán utilizar los corchetes para mayor comprensión.

Ejemplos:

(utilizando comandos) El hecho de entregar algo como salida se toma como condición lógica verdadera.

if ps ax | grep httpd | grep -v grep
then
echo "El web server está funcionando"
else
echo "El web server NO esta funcionando"
fi

(utilizando condiciones)

if [ -w /etc/passwd ]
then
echo "Tengo permisos de escritura en el archivo /etc/passwd"
else
echo "NO tengo permisos de escritura en el archivo /etc/passwd"
fi

Puede suceder que tengamos que testear mas de una condicion, para esto, podemos utilizar la siguiente estructura:

if condición1
then
Comando1
Comando2
elif condición2
then
Comando1
Comando2
else
Comando-por-defecto #Ninguna de las condiciones anteriores es verdadera
fi

Esta es una manera de abreviar varias estructuras if - then - else - if en una sola, pudiendo utilizarse más de dos condiciones.

La estructura de control if, es muy utilizada con test, que no es más que una operación de comparación, en las condiciones que se requieran.

"case"

El "case" es una proposición que puede analizar y validar varios casos (opciones) del valor de una variable.

Sintaxis

case variable in
patrón | patrón)
Comando;
Comando;
Comando;;

patrón | patrón)
Comando;
Comando;
Comando;;
*)
Comandos si ningún otro valor fue igualado;;
esac

El case es raramente usado, pero es muy eficiente en algunos casos.

Ejemplos:

case $mes in
ene*|Ene*)
echo "Mes de Enero";;
feb*|Feb*)
echo "Mes de Febrero";;
mar*|Mar*)
echo "Mes de Marzo";;
*)
echo "Algún otro mes!";;
esac

Ciclos

Los ciclos contienen secciones del programa que se repetirán una determinada cantidad de veces o hasta que alguna condición cambie.

for

El for debe ser el ciclo más utilizado, es muy práctico cuando se trabaja con shell scripts.

A diferencia de los lenguajes de programación más comunes, un ciclo for dentro de un shell script realiza una acción dada sobre cada uno de los elementos de una lista, y no sobre una variable que se va incrementando en cada ciclo.

La variable utilizada en el for (en este caso $i) es reemplazada por cada una de las palabras de la lista, en cada ciclo del for.

Sintaxis:

for i in lista de palabras
do
cuerpo del ciclo, $i tiene el valor de elementos sucesivos de la lista
done

Ejemplos:

for i in `ls -1 /tmp`
do
echo $i
rm -i $i
done

En este ciclo, el comando ls -1 /tmp, generará una lista de todos los archivos que existen en el directorio /tmp. Dentro del cuerpo del ciclo imprimimos el contenido de la variable y luego preguntamos si se desea borrar ese archivo (opción -i del comando rm).

while

Este ciclo utiliza la condición de terminación de un comando (valor de retorno) para controlar la ejecución de los comando dentro del ciclo. Termina la ejecución del ciclo, cuando el comando devuelve falso (algo diferente a 0).

Sintaxis:

while comando
do
cuerpo del ciclo ejecutado a condición de
que el comando devuelva verdadero
done

Ejemplos:

while sleep 60
do
who | grep daniel
done

En este ejemplo, cada 60 segundos (definidos por el comando sleep 60), verificará si el usuario daniel ha ingresado al equipo. En caso de que lo haya hecho, el listado del who saldrá por pantalla (cada 60 segundos).

until

Este ciclo se comporta de una manera muy similar al anterior, ya que define su control dependiendo del comando que ejecuta (si éste da verdadero, se sigue ejecutando el ciclo).

Sintaxis:

until comando
do
cuerpo del ciclo ejecutado a condición
de que el comando devuelva falso.
done

Ejemplo:

until who | grep daniel
do
sleep 60
done

En este ejemplo, a diferencia del ejemplo del ciclo while, el ciclo ejecuta primero el comando, de esta forma no es necesario esperar 60 segundos para saber si el usuario daniel esta logueado en el equipo (si el usuario esta logueado, el ciclo termina).

Argumentos

Los argumentos sirven para pasarle a un programa o una función valores desde la línea de comando.

Variable Descripción
$# Número de argumentos
$* Todos los argumentos del shell
$- Opciones suministradas al shell
$? Valor de retorno del último comando ejecutado
$$ Identificación del PID (número de proceso)
$0 Nombre del script
$1 Primer argumento
$n Argumento "n"

Ejemplo:

#!/bin/sh
#
# Programa que recibe argumentos y los imprime por pantalla
#
echo "\$*: $*"
echo "\$#: $#"
echo "\$0: $0"
echo "\$1: $1"
echo "\$2: $2"

Notas sobre el ejemplo:

En la líneas de impresión (echo), para imprimir el símbolo "$" (pesos) se tiene que anteponer el símbolo "\" (contra barra), sino el shell lo va a interpretar como una variable, y si esta existe imprimirá su contenido.

Funciones

Las funciones son un recurso esencial para la buena programación, permiten escribir una sola vez un pedazo de código que se repita varias veces en el script, y así, minimizar el margen de error y también la cantidad de líneas en el programa.

Para utilizarlas simplemente se hace un llamado a la función. Las funciones pueden estar dentro del mismo shell script, o en un archivo aparte. Cuando se escriben las funciones en un archivo aparte, es muy importante utilizar el comando "." (punto) para cargarlas en memoria. Si no se cargan en memoria de esta manera, las funciones no estarán disponibles.

Ejemplo:

#
# Cargando las funciones en memoria
#
. /home/jose/funciones/funciones-arch.sh

Uso

La sintaxis para utilizar funciones es muy sencilla y no agrega mayor dificultad. El modo de uso se remite a definir la función especificando el nombre y parentesis que abren y cierran. Todos los comandos que involucran la función se encierran con llaves.

Ejemplo:

(archivo: lib/arch.sh)

#
# Funciones para manipulación de archivos
#
borrar ()
{
arch=$1
if [ -z "$arch" ]
then
echo "No se recibió ningún archivo"
return 2
else
if [ -f "$arch" ]
then
if [ -w "$arch" ]
then
rm -i $arch
else
echo "No tengo permisos para borrar $arch"
fi
else
echo "$arch no es un archivo"
fi
fi
}

(archivo: principal.sh)

#!/bin/bash
#
# Programa ejemplo para el uso de funciones en shell script
#

#
# Cargo las funciones en memoria
#
. lib/arch.sh
dir=./
for i in $dir/*
do
borrar $i
done

Notas sobre el ejemplo:

(archivo: lib/arch.sh)

Esta función toma el archivo a borrar como argumento (arch=$1), verifica que se le haya pasado un argumento realmente (if [ -z "$arch" ]), que un archivo pasado como argumento sea regular (if [ -f "$arch" ]) y que tenga permisos de escritura. En todos los casos de falsedad responde con un mensaje avisando el problema.

(archivo: principal.sh)

El programa hace un ciclo con los archivos de un directorio específico (for i in $dir/*) y se los pasa como argumento a la función borrar.

Valores de retorno

Las funciones pueden devolver valores de error utilizando el comando "return ". En caso de que no se especifique un valor de retorno de esta forma, el valor retornado será el que retorne el último comando ejecutado dentro de la función. Desafortunadamente no es posible asignar el valor retornado en una sola línea, siempre es necesario utilizar la variable especial "$?".

Ejemplo:

#!/bin/sh

func () {
return $1
}

func 0
echo "ret: ($?)"
func 1
echo "ret: ($?)"
func 2
echo "ret: ($?)"
func 3
echo "ret: ($?)"

Variables locales a la función

Existe la posibilidad de utilizar variables locales a la función, esto significa que la variable solamente va a existir durante la ejecución de la función.

Para crear una variable local a la función se utilizar el operador "local ".

Ejemplo:

func () {
local x
x=$1
echo "Dentro de la función \$x vale ($x)"
}

echo "Antes de ejecutar la función \$x vale ($x)"
func HOLA!!
echo "Después de ejecutar la función \$x vale ($x)"

Operaciones Aritméticas

Existen varias formas de calcular valores dentro de un shell script. Tradicionalmente, estos cálculos se hicieron con programas externos, esto generaba un retardo inmenso en la ejecución del shell script.

Hoy los nuevos intérpretes traen la posibilidad de hacer cálculos internamente. Para esto se utiliza una sintaxis especial, y es muy importante que los valores de las variables que utilicen para hacer estos cálculos sean números únicamente.

Uso

La sintaxis para hacer operaciones aritméticas es la siguiente:

$[]

Las operaciones que se pueden realizar son:

  • suma $((1+1))
  • resta $((2-1))
  • multiplicación $((2*2))
  • división $((2/2])
  • otras como suma de bits, sacar el módulo, evaluación de igualdad, etc.

Ejemplo:

#!/bin/sh
#
# Operaciones aritméticas
#
x=2
tot=$[$x+1]
echo "tot: ($tot)"

bc

A veces hay conjuntos de herramientas que nos suenan y no las aprovechamos al máximo. Quizás una de esas sea bc. La calculadora que podemos usar desde la consola, que aparte de sumar y restar puede realizar cálculos con una precisión de varios decimales, cambiar de base numérica o programarla, etc. veremos como...

Si ejecutamos bc veremos:

$ bc
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.

Acá espera que trabajemos desde la entrada estándar, por ejemplo:

4+8
12
scale=3
27/68
.397
halt

Sabe sumar (y restar, dividir, multiplicar; sin problemas).

Variables

Si queremos que trabaje con decimales, asignaremos un valor a la variable scale.

scale=3
2/3
.666

También podemos hacer:

scale=9;2/3
.666666666

Al ser scale una variable más, podemos consultar su valor sencillamente escribiéndola:

scale
3
#o también usando print
print scale
3

Otro ejemplo de usos de variables podría ser:

scale=5
variable=3
100/variable
33.33333

Hay una variable especial llamada last, almacena el resultado de la última operación:

100/3
33.33333
last*2
66.66666

Cambios en la base numérica

Hay dos variables especiales que son ibase y obase que definen la base de entrada y de salida de los números, respectivamente.

Si queremos calcular el número 5 en base 10 (decimal) a base 2 (binario), haremos:

obase=2
5
101

Si queremos pasar de binario a hexadecimal:

obase=16
ibase=2
11111111
FF

Otras operaciones matemáticas

Podemos usar también sqrt, 2^3, etc.

Si queremos tener operaciones matemáticas más complejas tenemos que ejecutar el bc -l para cargar la librería matemática:

s (x)   Seno de x, en radianes
c (x) Coseno de x
a (x) Arcotangente de x
l (x) Logaritmo neperiano de x
e (x) Exponencial de x
j (n,x) Función de Bessel de un entero de orden n de x

Aplicación en un script

Si queremos pedir que el usuario teclee algo por teclado, la función es read. Por tanto.

variable=read()

Esperará que tecleemos algo y lo pondrá en variable.

También podemos hacer un programa en un archivo y ejecutarlo:

print "Hola\n"
print "Escribe tu año de nacimiento\n"
nacimiento=read()
edad=2003-nacimiento
print edad
print " años\n"
halt

Y lo ejecutamos con bc -q archivo.bc (el -q es para que no muestre el Copyright) estaremos preguntando el año nacimiento y mostrando la edad.

Ahora un ejemplo definiendo una función, pasándole un parámetro y retornándolo. Veremos que es totalmente intuitivo:

define mayor_edad(edad) {
if (edad <>= 18)
return 1
}

print "Dí tus edad\n"
edad=read()

if (mayor_edad(edad))
print "Eres mayor de edad\n"

if (!mayor_edad(edad))
print "Eres menor de edad\n"

halt

Un último ejemplo, con un for:

for (i=0;i<10;i++)> 

Observemos en la , para separar la variable y la cadena en el print.

Para salir

halt

Otra forma muy útil en los scripts es:

resultado=$(echo "scale=3;2/3" | bc)

Operaciones lógicas y de comparación


test

Para usar if podemos usar un nuevo elemento, los corchetes que evalúan las condiciones, esto esta basado directamente en test. Y éste nos sirve para comparar variables.

Por ejemplo:

-lt Menor que
-eq Igual que
-gt Mayor que
-le Menor o igual que
-ge Mayor o igual que
-ne No coinciden
-a Operador lógico and
-o Operador lógico or
!= Distinto

Sintáxis:

exp1 operador exp2

Uso de test:

test 8 -lt 9

Usando variables en test:

variable1=5
variable2=3
test $variable1 -gt $variable2

Otra forma muy útil de uso de test (si 3 es menor que 5):

[ 3 -lt 5 ]

O (si $HACER es distinto de 1):

[ $HACER != 1 ]

Ejemplo:

#!/bin/bash
variable1=5
variable2=3
[ $variable1 -lt $variable2 ]
echo $?

Aqui hemos evaluado dos variables para ver si la variable1 era menor que la variable2 y para ver el resultado hemos acudido a la “variable de retorno”, que nos mostrara 0 o 1.

Cuando necesitamos utilizar la negación, debemos usar el signo !

Ejemplo:

 if [ ! -x $FILE ]
then
chmod +x $FILE
else
echo "$FILE es ejecutable"
fi

Si el archivo no es ejecutable, le damos permiso de ejecución, sino, imprimimos el mensaje por pantalla

Interacción con el usuario

Muchos programas no serían factibles si no tuviéramos algún mecanismo para interactuar con el usuario, ya sea un simple "Presione Enter para continuar" o algo más sofisticado, como una lista de opciones de las cuales escoger.

Cuando el programa está escrito en shell, es muy sencillo lograr ambas cosas, utilizando dos herramientas: read y dialog.

read

El comando read es muy sencillo. Le indicamos que pida el valor de una variable al usuario, el usuario escribe una línea de texto (es decir, cualquier cosa hasta que presione enter), y la variable toma el valor que el usuario ingresó.

Ejemplo:

[jose@xtech experto]$ read V
Hola mundo!
[jose@xtech experto]$ echo $V
Hola mundo!
[jose@xtech experto]$

Si deseamos sólo un "Presione enter para continuar" es exactamente lo mismo, simplemente ignoramos el valor de la variable :-)

dialog

Dialog es un programa que crea una "interfaz" para que el usuario interactúe, y entrega por la salida estándar el resultado de la acción del usuario, variable $?.

Puede producir preguntas si/no, menú, lista, calendario, barra de progreso, diálogo de contraseña, cuadro de texto, cuadro de mensaje, etc. Recomendamos leer la documentación del manual (man dialog) o ejecutar

dialog --help

Aparte del programa "dialog", que produce una salida por consola, puede ser que tenga en su sistema un programa "gdialog", Xdialog o "dldialog" (dependiendo del Linux que utilice), que son lo mismo, solo que abren una ventana gráfica, por X11, para el diálogo.

Sintaxis:

dialog  { --opciones específicas }

Ejemplo 1 (Diálogo "yesno"):

[jose@xtech experto]$ dialog --yesno "Desea salir?" 6 30

Produce esto:

Los argumentos 6 y 30 corresponden a la altura y ancho, respectivamente, del cuadro. En su versión X11, sería:

[jose@xtech experto]$ gdialog --yesno "Desea salir?"

Y se vería así:

Ejemplo 2 (Diálogo "inputbox"):

[jose@xtech experto]$ dialog --inputbox "Ingrese su nombre" 9 30 Juan

Note que agregamos el argumento "Juan", que será el valor predeterminado para este inputbox:

O también:

[jose@xtech experto]$ gdialog --inputbox "Ingrese su nombre"

Produce esto:

El caso del "inputbox" suele tener aparejado la necesidad de asignar ese valor ingresado a una variable. Para hacer eso tendremos que usar la siguiente sintáxis:

VARIABLE=`gdialog --inputbox "Ingrese su nombre" 2>&1`

Note que pusimos todo el comando entre comillas invertidas para que reemplace el mismo por el valor que el comando representa después de ejecutarse.

El 2>&1 es un truco que permite redirigir la STDERR hacia la STDOUT, ya que el valor ingresado sale por la salida de error en vez de la estándar.

Ejemplo 3 (Diálogo "msgbox"):

[jose@xtech experto]$ dialog --msgbox "Esto es muy bueno para mensajes" 5 50

Produce esto:

O también:

[jose@xtech experto]$ gdialog --msgbox "Esto es muy bueno para mostrar mensajes
largos o de advertencia"

Produce esto:

Ejercicio 1

Descompresión Automática de Archivos


Se debe ingresar el nombre de un archivo por la línea de comandos, y el script debe reconocer con qué herramienta está comprimida y proceder a descomprimirlo. Si el archivo no está comprimido, el script deberá devolver un mensaje de error.

TIP: utilizar el programa "file"

Resolución
#!/bin/bash
#
# Decompresor inteligente

if [ -z $1 ]
then
#
# Si no hay argumento salimos del script con error "1!
#
echo "Debe ingresar el nombre del archivo"
exit 1
else
ARCHIVO=$1
fi

TIPO=`file $ARCHIVO`
#
# Verificamos el tipo de archivo
# Salida del comando file:
#
# Para GZIP
# file ejemplo.gz
# ejemplo.gz: gzip compressed data, was "ejemplo",from Unix
#
# Para BZIP2
# file ejemplo.bz2
# ejemplo.bz2: bzip2 compressed data, block size =900k


case $TIPO in
"${ARCHIVO}: bzip2"*)
bunzip2 $ARCHIVO
;;
"${ARCHIVO}: gzip"*)
gunzip $ARCHIVO
;;
*)
echo "No esta comprimido"
;;
esac


exit 0

Ejercicio 2

Listado del Directorio Home


Se debe realizar un listado recursivo del directorio personal, y guardar la información en un archivo. Luego debe comprimirse ese archivo y preguntar al usuario en qué directorio quiere guardar el archivo comprimido.

Resolución
#/bin/bash
# Listado del Home

# Iniciamos la variable.
rm -rf ./listado.txt
touch ./listado.txt
ARCHIVO="./listado.txt"

# Listamos los archivos y los guardamos en el archivo
ls -1 ~ >> $ARCHIVO
cat $ARCHIVO

# Comprimimos el archivo como tar.gz
tar -czvf listado.tar.gz $ARCHIVO
COMPRIMIDO=listado.tar.gz

# Preguntamos al usuario donde desea guardar ese archivo.
echo "Por favor, ingrese el PATH donde desea almacenar $COMPRIMIDO."
read PATH
echo $PATH
echo $COMPRIMIDO

# Guardamos el archivo donde se indico.
mv $COMPRIMIDO $PATH

# Se imprime donde se guardo el archivo.
echo "Se guardo $COMPRIMIDO en $PATH"

exit 0

Ejercicio 3

Ralización de un backup diario


Se debe archivar como "tarball" (archivo .tar.gz) todos los archivos del directorio personal. Esto se debe hacer grabando un CR regrabable.

Resolución 1
#!/bin/bash
#
# Se limpia la pantalla para tener una salida más prolija.

clear

# Se procede a desmontar el CD por si alguien olvidó hacerlo.

echo "*** Desmontando CDROM ***"
umount /mnt/cdrom

# Se limpia nuevamente la pantalla.

clear

# Se anuncia al usuario que se comienza a borrar el CD.

echo "*** Borrando CDROM ***"
echo "Por favor, sea paciente. Este proceso puede demorar hasta 20 minutos."
echo " "

# Se utiliza el programa cdrecord para borrar el CD.

cdrecord -v -dev=ATAPI:0,0,0 -blank=fast > /dev/null

# Se borra nuevamente la pantalla.

clear

# Se anuncia por pantalla el comienzo del backup del directorio.

echo "*** Comenzando el backup ***"

# Se crea un directorio temporal para almacenar el backup.

mkdir /tmp/backup02

# Se limpia la pantalla.

clear

# Se anuncia por pantalla que se comienza a salvar el directorio personal.

echo "*** Backup del directorio Personal ***"

# Se comprime el directorio personal.

tar -cvzf /tmp/backuo02/home.tar.gz ~/ > /dev/null

# Se limpia la pantalla.

clear

# Se anuncia por pantalla que se realiza el ISO a grabar.

echo "*** Creando archivo de Resguardo ***"

# Se crea la imágen ISO.

mkisofs -r -J -o backup02.iso backup02/ > /dev/null

# Se borra la pantalla.

clear

# Se anuncia el comiezo de la grabación del CD.

echo "*** Comenzando la grabacion del CDROM ***"
echo "Por favor, sea paciente. Este proceso puede tardar hasta 30 minutos."
echo " "

# Se comienza a grabar el CD con cdrecord.

cdrecord -v speed=4 dev=ATAPI:0,0,0 -data backup02.iso > /dev/null

# Se anuncia en pantalla el borrado de los archivos auxiliares.

echo "*** Limpiando archivos extras ***"
rm -Rf /tmp/backup02.iso > /dev/null
rm -Rf /tmp/backup02 > /dev/null

# Se desmonta el CD.

umount /mnt/cdrom

# Se limpia la pantalla.

clear

# Se anuncia que el proceso finalizó con éxito.

echo "*** Se realizo el backup con exito ***"

exit 0
Resolución 2 (backup por SAMBA)
#!/bin/bash
#
# Se limpia la pantalla para tener una salida más prolija.

clear

# Se procede a desmontar el CD por si alguien olvidó hacerlo.

echo "*** Desmontando CDROM ***"
umount /mnt/cdrom

# Se limpia nuevamente la pantalla.

clear

# Se monta por red el CD de un cliente windows.

smbmount //pc3/d /mnt/cdrom -o username=usuario,password=contraseña

# Se borra nuevamente la pantalla.

clear

# Se anuncia por pantalla el comienzo del backup del directorio.

echo "*** Backup del directorio Personal ***"

# Se comprime el directorio personal.

tar -cvzf /mnt/cdrom/home.tar.gz ~/ > /dev/null

# Se desmonta el CD.

umount /mnt/cdrom

# Se limpia la pantalla.

clear

# Se anuncia que el proceso finalizó con éxito.

echo "*** Se realizo el backup con exito ***"

exit 0

7/05/2008


Curso de programación en C para GNU/Linux


Curso de programación en C para GNU/Linux realizado por Pablo Garaizar Sagarminaga.

El curso comprende los siguientes apartados:

  • Llamadas al sitema
  • Programas, procesos, hilos
  • Estructuras de datos
  • Estados de los procesos en Linux
  • Identificativos de proceso
  • Planificación
  • El GCC
  • Compilación básica
  • Librerías
  • Optimizaciones
  • Debuggin
  • Make y makefile
  • Programando en C para GNU/Linux
  • Llamadas sencillas
  • Manejo de directorios
  • Permisos
  • Creación y duplicación de procesos
  • Comunicación entre procesos
  • Comunicación por red

Descargar

Fuente: http://www.lugsaju.com.ar/2008/07/curso-de-programacin-en-c-para-gnulinux.html

4/11/2008


Programación Gráfica GTK2/Ruby en Gnu/Linux (I)

Bueno mi necesidad de aprender a programar en algún lenguaje para Gnu/Linux en modo gráfico viene desde hace tiempo, hay muchas maneras de crear GUIs (Interfaz Gráfica para el usuario), la mayoría de los lenguajes como C, Python, Perl, Php tienen librerías para crear aplicaciones Gtk2, las usadas por Gnome. Pero como me gané un librito de Ruby hace unos meses y lo tenía tirado por ahí decidí echarle una mirada y me gustó =D. Por eso lo que valla aprendiendo voy a ir subiéndolo a mi blog, así usted lector que no tiene conocimientos de programación gráfica en Gnu/Linux va a aprender junto conmigo, la creación de GUIs usando Ruby. La diferencia de muchos tutoriales donde el maestro se la sabe todas, yo solo voy a aportar mis conocimientos adquiridos al mismo paso que usted. Espero que les guste y les ayude. No me voy a centrar en la progración de Ruby en modo consola, solo explicaré conceptos si fueran necesarios para nuestra aplicación gráfica. Si algún programador con experiencia en gtk2/ruby o ruby quiere aportar, se encuentra bienvenido...

Empecemos, primero, ¿Qué es Ruby?


Ruby es un lenguaje de programación reflexivo y orientado a objetos(lenguaje interpretado), creado por el programador japonés Yukihiro "Matz" Matsumoto, quien comenzó a trabajar en Ruby en 1993, y lo presentó públicamente en 1995. Combina una sintaxis inspirada en Python, Perl con características de programación orientada a objetos similares a Smalltalk. Comparte también funcionalidad con otros lenguajes de programación como Lisp, Lua, Dylan y CLU. Ruby es un lenguaje de programación interpretado en una sola pasada y su implementación oficial es distribuida bajo una licencia de software libre.(es.Wikipedia)

Su sitio oficial no está nada mal, además también está en spanish...creo que eso me enamoró =P. Me voy a centrar mucho en la práctica por lo que si quieren saber sobre la historia de Gtk2 o de Ruby busquen en la web XD. NO voy a enseñar a fondo ruby, solo vamos a obtener conocimientos para desarrollar GUI, que algún experto no me reclame después..=P.

Instalación:

Arch Linux:

# pacman -S ruby ruby-gnome2

Debian o Ubuntu:

# apt-get install ruby ruby-gnome2

Ya estoy ansioso por empezar, asi que empecemos, a me olvidada, vamos a utilizar un editor de texto, a mi me gusta Vim pero pueden usar Emacs, Gedit o Mousepad o lo que le guste, guarden su programa como "holamundo.rb".

Creando el famoso "Hola mundo":

#!/usr/bin/env ruby
=begin
holamundo.rb
Nuestro Primer Programa =)
=end

require 'gtk2'

boton = Gtk::Button.new("Hola Mundo")
boton.signal_connect("clicked"){
puts "Me hicieron click"
}

ventana = Gtk::Window.new
ventana.signal_connect("delete_event"){
puts "Ventana cerrada"
#true
false
}

ventana.signal_connect("destroy"){
puts "Ventana destruida"
Gtk.main_quit
}

ventana.border_width = 10
ventana.add(boton)
ventana.show_all

Gtk.main


Explicación:

La primera línea de un programa en la mayoría de los sistemas *nix, le indica al sistema que lenguaje va a ser utilizado.

#!/usr/bin/env ruby

La siguiente estructura es un comentario ya que se encuentra entre =begin y =end. Otra manera de comentar es agregando # al principio de una línea.

=begin
holamundo.rb
Nuestro Primer Programa =)
=end

La siguiente línea vincula a la librería Ruby/GTK2 con nuestro programa:

require 'gtk2'

A continuación vemos lo siguiente:

boton = Gtk::Button.new("Hola Mundo")

Nos indica que creamos un objeto llamado "boton" que posee las características de un "Gtk::Button.new" y mostrará en su título "Hola Mundo".

button.signal_connect("clicked") {
puts "Me hicieron click"
}

Cuando hagamos click sobre nuestro botón ("boton.signal_connect("clicked")") mostrará en la terminal "Me hicieron click".

Ahora vamos a crear un objeto que sea del tipo "Gtk::Window.new", es decir una nueva ventana GTK, llamada "ventana":

ventana = Gtk::Window.new

Los parámetros normales o defaults son los siguientes:
  • Tamaño: 200x200
  • Estilo: Gtk::Window::TOPLEVEL
  • Título: el mismo que el nombre del programa "holamundo.rb"
Sigamos:

ventana.signal_connect("delete_event"){
puts "Ventana cerrada"
#true
false
}

Cuando la ventana sea cerrada, nos mostrará en la consola "Ventana cerrada", luego la línea que dice "false" quiere decir que la ventana desaparezca, prueben comentándola y descomentar #true a ver que sucede. Es decir de la siguiente manera podría probar =P :

ventana.signal_connect("delete_event"){
puts "Ventana cerrada"
true
#false
}

Una vez que la ventana fué cerrada exitosamente nuestro programa destruirá el objeto ventana y saldrá del programa mediante "Gtk.main_quit".

ventana.signal_connect("destroy"){
puts "Ventana destruida"
Gtk.main_quit
}

Por último nos queda las últimas 4 líneas:

ventana.border_width = 10
ventana.add(boton)
ventana.show_all

Gtk.main

La primera nos indica el ancho del borde, la segunda agrega nuestro objeto "boton" creado anteriormente a nuestro objeto ventana y la tercera nos muestra la ventana en pantalla con todos sus objetos.

Por último nos queda "Gtk.main" que será necesario en cada aplicación creada, ya que es otra llamada que verá Ruby/GTK2 en cada aplicación. Cuando el control llega a este punto, GTK se dormirá en espera de eventos X (como un botón o la presión de una tecla), el tiempo, o eventos en el archivo IO
de notificaciones.
En el ejemplo anterior, sin embargo, los eventos son ignorados.

Para ejecutar nuestro programa abrimos una terminal:

$ ruby holamundo.rb

Bueno terminamos nuestro primer programa, creo que así les habrá quedado...


Espero que este sea el primero de muchos artículos sobre ruby, y bueno, si a alguien le resultó util o le gustó o no, o si me equivoqué en algo me gustaría que dejaran sus comentarios. Saludos a todos y hasta la próxima =) !!

Referencia

3/10/2008


Expresiones Regulares en la shell. Ejemplos de uso con grep, awk y sed.

Estaba intentando hacer un CGI muy sencillo usando bash (como lo que quería hacer era algo muy específico de bash, no he querido usar ni Perl, ni PHP). La única dificultad que he encontrado ha sido obtener las distintas variables que el método GET pasa al CGI a través de la variable $QUERY_STRING, que normalmente tiene este aspecto:

QUERY_STRING='parametro1=valor1&parametro2=valor2&parametro3=valor3'

Estaba yo ya comenzando a darle vueltas a una Expresión Regular para interpretar dicha salida cuando he decidido buscar en Google para ver si alguien tenía una solución más completa que la que yo ya tenía a medias. He llegado al artículo CGI Scripting Tips for Bash or SH que propone lo siguiente para extraer del QUERY_STRING la variable que nos interese:

PARAMETROX=`echo "$QUERY_STRING" | grep -oE "(^|[?&])parametrox=[^&]+" | sed "s/%20/ /g" | cut -f 2 -d "="`

Y es que con bash y sus compañeros sed, awk, cat, grep, etc. aún se puede hacer casi de todo, incluso un weblog o un servidor web. Como las Expresiones Regulares son tan excepcionalmente útiles como complicadas a la hora de usarlas, me he decidido a comentar algunos ejemplos, comenzando por el que me ha llevado a este artículo.

Y antes de seguir conviene comentar que la opción -o del grep del comando anterior indica que la salida del comando ha de contener únicamente la parte de texto que coincide con el patrón, no toda la línea. La opción -E indica que se va a usar una Expresión Regular Extendida (egrep es lo mismo que grep -E).

Pasemos a analizar la expresión regular… (^|[?&])parametrox=[^&]+ significa que buscamos algo que:

^|[?&] está a principio de línea ^ o | que comienza por un caracter ? o un &
parametrox= seguido por la secuencia de caracteres parametrox=
[^&]+ y que acaba con uno o más caracteres + que no sean &

Ejemplos de Expresiones Regulares

La página http://www.regular-expressions.info es un excelente sitio para aprender sobre expresiones regulares. Allí he encontrado algunos ejemplos que me han gustado Éstos son tres de ellos especialmente útiles:

Buscar una dirección de e-mail

\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b

\b[A-Z0-9._%-]+ Buscamos un comienzo de palabra \b que contenga uno o más caracteres + dentro del conjunto A a Z, 0 a 9,., _, % y -, que son los caracteres admitidos para un nombre de usuario
@ seguido del símbolo @
[A-Z0-9.-]+ seguido por uno o más + caracteres A a Z, 0 a 9,. y -, que son los caracteres admitidos para un dominio
\. seguido por un punto escapado con \, ya que el punto solo significa cualquier caracter
[A-Z]{2,4}\b seguido por entre 2 y 4 caracteres {2,4} entre el conjunto A-Z y además éste tiene que ser el final de una palabra \b

Por tanto, si usáramos esa expresión regular con egrep sobre un texto como este:

En un lugar de la Mancha, de cuyo nombre no quiero acordarme, pero a cuyo ayuntamiento podíamos enviar e-mails a la dirección ayuntamiento@lugardelamancha.es, no ha mucho tiempo que vivía un hidalgo de los de lanza en astillero, adarga antigua, rocín flaco y galgo corredor cuyo e-mail era don.quijote@del.toboso.edu.

obtendríamos:

$ egrep -oi '\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}' donqui.txt
ayuntamiento@lugardelamancha.es
don.quijote@del.toboso.edu

Buscar dos palabras cercanas en un párrafo

Si consideramos que dos palabras cercanas son aquellas que están separadas entre 1 y 8 palabras, esta expresión regular es perfecta:

\bpalabra1\W+(w+\W+){1,8}palabra2\b

\bpalabra1 Buscamos un comienzo de palabra con la sucesión de caracteres palabra1
\W+ seguido por uno o varios caracteres no alfanuméricos \W
(\w+\W+){1,8} seguido de entre 1 y 8 grupos {1,8} de uno o más caracteres alfanuméricos \w y uno o más caracteres no alfanuméricos \W
palabra2\b acabado un la sucesión de caracteres palabra2

Y podemos probar la expresión sobre el texto anterior:

$ egrep -o '\bhidalgo\W+(\w+\W+){1,8}lanza\b' donqui.txt
hidalgo de los de lanza

Por supuesto, puede ser que no sepamos si palabra1 va antes que palabra2, así que podemos especificar que una cosa o | la otra:

\b(palabra1\W+(\w+\W+){1,8}palabra2|palabra2\W+(\w+\W+){1,8}palabra1)\b

Buscar una dirección IP

\b[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\b

Creo que este ejemplo ya no necesita explicación, aunque tiene un defecto: la expresión encajaría también con 999.999.999.999. Para corregirlo, podríamos sustituir cada bloque [0-9]{1,3} por (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?) que significa que cada número puede ser del 250 al 255 25[0-5] o del 200 al 249 2[0-4][0-9] o del 0 al 199, contemplando la posibilidad de que por ejemplo un 7 se escriba 7, 07 o 007 [01]?[0-9][0-9]?. El símbolo ? indica uno o ninguno.

Podemos obtener fácilmente una lista de IPs que han visitado nuestro servidor web con el siguiente comando:

$ egrep -o '\b[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\b' access.log | sort -u

Ejemplos de uso de las Expresiones Regulares en la shell

En el uso diario de la shell, las expresiones regulares son nuestra navaja suiza. Ya hemos visto algunos ejemplos con grep, pero sigamos con algunos más de cosecha propia que usan grep, awk y sed.

Lista de usuarios con password a partir del /etc/shadow

Resulta que queremos saber los usuarios del fichero /etc/shadow que pueden logearse al sistema (que no tienen un caracter ! o * en el campo del password). La expresión regular a usar sería:

# egrep '^[^:]+:[^*!]+:' /etc/shadow
fulano:$1$PFC1F2pj$0KEflTjNmDQg9.rtFT/DK0:12793:0:99999:7:::
mengano:$1$aAUa4js/$c2LEh.HeT0JvCG3B.34O.:12793:0:99999:7:::

^[^:]+ Buscamos uno o más caracteres + que no sean : [^:] a principio de línea ^
: seguido de un caracter :
[^*!]+ seguido por una secuencia de uno o más caracteres que no sean ! o *
: seguido de otro caracter :

Podemos hacerlo un poco mejor, porque sólo nos interesa ver el nombre del usuario. Para ello podríamos hacer un cut -f 1 -d : sobre la salida anterior, pero usando el awk somos capaces de hacer lo anterior en un sólo comando:

# awk -F":" '$2~/[^!*]+/ { print $1 }' /etc/shadow
fulano
mengano

En el comando anterior le decimos a awk que, usando como separador de campos el caracter : (-F”:”) queremos que nos saque el primer campo { print $1 } siempre que el segundo campo cumpla la regla de que contenga uno o más caracteres que no sean ! o * $2~/[^!*]+/.

Extraer los referer del access.log

Si tenemos líneas como la siguiente (LogFormat combined) en el access.log de nuestro apache:

192.168.1.3 - - [12/Apr/2007:22:15:14 +0200] "GET /blog/ HTTP/1.1" 200 46503 "http://www.google.es/search?hl=es&q=vicente+navarro" "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.3) Gecko/20070310 Iceweasel/2.0.0.3 (Debian-2.0.0.3-1)"
192.168.1.3 - - [12/Apr/2007:22:24:07 +0200] "GET /blog/ HTTP/1.1" 200 46503 "http://www.google.es/search?q=super+coco&hl=es&start=10&sa=N" "Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1.3) Gecko/20070310 Iceweasel/2.0.0.3 (Debian-2.0.0.3-1)"

podemos obtener un listado de los referers que han traído a los visitantes a la página con el siguiente comando:

$ egrep -o '^[0-9.]+ - - \[.+\] \"[^"]+\" [0-9-]+ [0-9-]+ \"[^"]+\"' access.log | egrep -o '\"[^"-]+\"$' | egrep -o '[^"]+' | sort -u
...
http://www.google.es/search?hl=es&q=vicente+navarro
http://www.google.es/search?q=super+coco&hl=es&start=10&sa=N
...

Hay dos grep’s, porque estamos identificando y extrayendo todo el texto hasta llegar al referer para dejarlo al final y después extraerlo con el segundo grep. Pero aunque sirva para ilustrar la entrada, resulta que nos hemos complicado muchísimo la vida, porque el trozo del referer es el único que comienza con http://. Teniendo en cuenta esto, el comando, con una Expresión Regular mucho más simple, queda:

$ egrep -o 'http://[^"]+' access.log | sort -u

Y usando sólo awk:

awk '$0~/http/ { print $11 }' access.log | awk -F'"' '{ print $2 }' | sort -u

Pero ahora queremos saber a partir de los referers que hemos obtenido anteriormente, con qué parámetros de búsqueda y en qué posición aparece nuestra página en Google. Para ello, podríamos usar la Expresión Regular del principio para obtener los parámetros q= y start= de la URL de Google, pero hagámoslo con awk:

$ egrep -o 'http://[^"]+' big | sort -u | awk -F "[?&=]" '$1~/google.+search/ { TERMINOBUSQUEDA=""; POSICION="0"; for (i=2; i<=NF; i++) { if ($(i-1)~"(^|^as_)q$") { TERMINOBUSQUEDA=$i; } if ($(i-1)~"^start$") { POSICION=$i; } } print TERMINOBUSQUEDA " " POSICION; }' super+coco 10 vicente+navarro 0

Es decir, si la línea es una búsqueda de Google, el awk ha de hacer:

TERMINOBUSQUEDA="";
POSICION="0";
for (i=2; i<=NF; i++) { if ($(i-1)~"(^|^as_)q$") { TERMINOBUSQUEDA=$i;} if ($(i-1)~"^start$") { POSICION=$i; } } print TERMINOBUSQUEDA " " POSICION;

Expresiones Regulares de Perl (PCRE)

Las Expresiones Regulares de Perl (PCRE) son más potentes que las Expresiones Regulares POSIX. En algunas distribuciones, el grep -P nos permite usarlas, pero en Debian no es posible usar la opción -P de grep. En cambio, tenemos el pcregrep que sí que nos lo permite. Y una de las cosas más interesantes que se puede hacer con estas reglas es Lookahead y Lookbehind. Supongamos que queremos sacar de las URL de Google el valor del parámetro q= con un sólo comando, a diferencia de la expresión del principio que requería un cut adicional:

$ echo "http://www.google.es/search?q=super+coco&hl=es&start=10&sa=N" | pcregrep -o '(?<=[&?]q=)[^&?]+' super+coco

Usando PCRE, algo como (?<=delante)detras significa que para coincidir, la cadena tiene que ser delantedetras, pero delante no aparecerá en la cadena coincidente. ¡Muy útil! La pena es que este tipo de expresiones no se puede usar con cadenas de longitud variable:

$ pcregrep -o '(?<=\w+)prueba' fichero.txt pcregrep: Error in command-line regex at offset 7: lookbehind assertion is not fixed length

Ejemplos con sed y el /etc/passwd

sed es otra utilidad muy versátil. Su función más típica es la de sustitución, aunque también puede hacer cosas sencillas pero útiles como mostrar sólo un conjunto de líneas de un fichero o eliminarlas, insertar texto al final de una línea determinada o reemplazar el texto de esa línea..

La sintaxis típica de sed para reemplazos es:

sed -r 's/reemplazada/reemplazo/g'

El -r es paraque el sed use Expresiones Regulares Extendidas o POSIX y el g significa que la sustitución se hará todas las veces que se encuentre la Expresión Regular en la línea, no sólo la primera vez. Si nos viene mal usar el carácter / para no tener que escapar, por ejemplo, muchas barras de path, podríamos usar cualquier otro carácter: s_reemplazada_reemplazo_g.

Por ejemplo, si quisiéramos cambiar la shell por defecto a todos los usuarios que aparecen en /etc/passwd, podríamos hacer:

$ sed -r 's/[^:]+$/:\/bin\/bash/g' /etc/passwd
...
mail:x:8:8:mail:/var/mail::/bin/bash
news:x:9:9:news:/var/spool/news::/bin/bash
uucp:x:10:10:uucp:/var/spool/uucp::/bin/bash
...

Y si queremos añadir una cabecera de “usuario_” a todos los nombres de usuario del sistema, podemos usar el carácter & para usar la parte coincidente en la cadena de reemplazo:

$ sed -r 's/^[^:]+/usuario_&/g' /etc/passwd
...
usuario_mail:x:8:8:mail:/var/mail:/bin/sh
usuario_news:x:9:9:news:/var/spool/news:/bin/sh
usuario_uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
...

Y como lo que pongamos entre paréntesis () en la cadena reemplazada, el sed nos lo guarda para usar en la cadena de reemplazo en \1, \2,… \9, supongamos que queremos mover todo a un nuevo sistema de ficheros y queremos cambiar tanto el home del usuario como su shell:

$ sed -r 's/([^:]+):([^:]+)$/:\/newfs\1:\/newfs\2/g' /etc/passwd
...
mail:x:8:8:mail::/newfs/var/mail:/newfs/bin/sh
news:x:9:9:news::/newfs/var/spool/news:/newfs/bin/sh
uucp:x:10:10:uucp::/newfs/var/spool/uucp:/newfs/bin/sh
...

Enlaces relacionados

Actualización 14/4/07: Un pobrecito hablador (¡muchas gracias!) comenta en la bitácora de Super Coco en Barrapunto otra interesante forma de obtener un parámetro de la QUERY_STRING:

PARAMETROX=`echo "$QUERY_STRING" | sed -r 's/.*\bparametroX=([^&]+).*/\1/'`

Actualización 9/11/07:

Supongamos que encontramos una página que ofrece muchos enlaces a ficheros para descargar de la red P2P eDonkey y nosotros queremos usar el aMule para hacerlo sin tener que copiar o pegar todos los enlaces o hacer click sobre todos ellos. Los enlaces ed2k aparecen en el código HTML de la página así:

fichero de la red eDonkey

Pues bien, tras guardar el HTML de la página en cuestión a disco, queremos extraer dichos enlaces para usarlos en el comando ed2k de aMule. Si supiéramos que no hay más de un enlace por línea podríamos hacerlo simplemente con un grep:

ed2k $(grep -E -o 'ed2k://[^/]+/' pagina.html)

Pero como es muy posible que no sea así, casi mejor lo hacemos con un awk:

ed2k $(awk -F '"' '/ed2k/ { for (i=1; i<=NF; i++) if ($i~"^ed2k") { print $i } }' pagina.html)

Actualización 26/11/07:

Extraer el texto existente entre etiquetas de un fichero HTML:

# pcregrep -o '(?<=)(.*?)(?=)' index.html
prueba

Si la probamos con el pcregrep, sólo sacará como máximo una coincidencia por línea y no funcionará si las etiquetas de principio y final están en líneas diferentes.

Cuando la etiqueta de principio tenga algún parámetro como por ejemplo: , la expresión no funcionará. Sin embargo, con algo como:

# pcregrep -o ']*>(.*?)' index.html
prueba

podríamos extraer el texto con las etiquetas, pero hemos tenido que quitar los lookahead y lookbehind porque si no:

# pcregrep -o '(?<=]*>)(.*?)(?=)' index.html
pcregrep: Error in command-line regex at offset 19: lookbehind assertion is not fixed length
Fuente:
http://www.vicente-navarro.com/blog/2007/04/13/expresiones-regulares-en-la-shell-ejemplos-de-uso-con-grep-awk-y-sed/