Categoría: Bash

Lenguajes de programación

http://programming-motherfucker.com/
http://programming-motherfucker.com/

Hace unos días estuve buscando un manual de programación de java,Perl y Ruby. Después de buscar y no encontrar manuales en español que me gustaran opté por buscar un manual en inglés y que lo hiciera por el camino difícil y me encontré con esta página: Programming motherfucker. El nombre no es que sea muy bonito, pero tiene buenos manuales.

Anuncios

Fichero de configuración simple

Cuando creamos un script a veces queremos que éste pueda cargar su configuración desde un fichero, de manera que al volver a iniciar no necesite la intervención del usuario. Esto se puede conseguir con el comando declare y un bucle while.

En el siguiente ejemplo la información será cargada desde un fichero de texto, que contiene el nombre de la variable y su valor, el fichero contiene el nombre de las variables sin espacios alrededor del signo de asignación que es como bash reconoce las variables correctamente.

Cargando la configuración

Contenido del fichero:


start=0
warn_sig=1
debug=no
sound=no

Para cargar las variables de éste fichero en nuestro script sería suficiente con unas pocas líneas:


while read variables
do
declare "$variables"
done < fichero_de_configuración.cfg

La ventaja de utilizar éste método para cargar la configuración del script es que es muy simple y no complica el código, pero tiene como desventaja que si el usuario toca manualmente el fichero de configuración y deja algún espació alrededor del signo de asignación ( = ) se produce un error en el script y adiós al invento :S.

Prevenir los espacios en blanco

Para evitar los problemas que pueden causar los espacio en blanco alrededor del signo de asignación podemos utilizar los comandos cut y sed. Para separar los campos utilizaremos cut, y para borrar los espacios en blanco el comando sed.

Utilizando el código anterior más unas pequeñas modificaciones podemos prevenir los espacios en blanco:


while read variables
do
nom=$(echo "$variables" | cut -d\= -f1 | sed "s/ //g")
valor=$(echo "$variables" | cut -d\= -f2)
declare "${nom}=${valor}"
done < fichero_de_configuración.cfg

En la variable nom cortamos el primer campo con el separador con el signo de asignación y borramos los espacios en blanco, el primer campo debe ser el nombre con el que la variable será cargada (mirar el contenido del fichero de ejemplo), y el segundo campo será el valor de la misma.

La variable valor se limita a seleccionar el segundo campo y a no borrar los espacios en blanco para no dañar el valor de la variable si es una cadena.

Seguramente existan centenares de maneras distintas de cargar la configuración de un script desde un fichero de texto plano, pero siempre hay que intentar seguir el principio KISS.

Keep It Short and Simple

Protege tus archivos con openssl!!

La necesidad de cifrar los datos contenidos en un equipo sea una empresa o un simple usuario, es imprescindible para proteger los datos personales de las personas ( o empresas ) que se encuentran registrados en esos ficheros, utilizando diversas técnicas de protección, entre la cuales se encuentra el cifrado de los datos.

En ocasiones queremos cifrar unos determinados datos desde la terminal, como por ejemplo: un fichero de texto o un fichero multimedia. Podemos utilizar un sistema de cifrado simétrico (o asimétrico) con la aplicación openssl.

Instalar openssl:

sudo apt-get install openssl

Openssl

Openssl es una herramienta de cifrado de datos y generación de certificados ssl, que permite cifrar archivos con un sistema de cifrado simétrico o asimétrico, en éste post sólo se mostrará un pequeño ejemplo de uso, porque el objetivo principal es el de cifrar los datos de una manera rápida y fácil.

Para cifrar ficheros con Openssl utilizamos los siguientes parámetros:

openssl enc -algoritmo_de_cifrado- -in fichero -out fichero_salida

El descifrado de los archivos es idéntico al cifrado sólo que agregando el parámetro -d:

openssl enc -algoritmo_de_cifrado -d -in fichero -out fichero_salida

¿Como sé que el usuario ha introducido la contraseña correcta?

Podemos saber si el usuario ha introducido la contraseña correcta mirando el valor devuelto por el último comando ejecutado ( en nuestro caso el comando openssl con la opción de desencriptar ), los programas que acaban correctamente su ejecución acostumbran a devolver el valor 0, y cuando ha ocurrido un error se da un número distinto a 0.

Openssl desencriptando un fichero con una clave incorrecta:

fichero=”hola.png.enc”

pass=”incorrecto”

openssl enc -aes-256-cbc -d -pass pass:”$pass” -in “$fichero” -out “${fichero%%.enc}” 2> /dev/null

if [ “$(echo $?)” -ne 0 ]; then echo Ha ocurrido un error;fi

Facilitar el cifrado y descifrado de archivos

Si ponemos el código anterior dentro de un bucle infinito podemos darle la oportunidad al usuario de volver a introducir la contraseña por si se ha equivocado al teclear la contraseña, en el siguiente script se muestra como al combinar zenity, el comando gksu y openssl podemos hacer un pequeño script que nos facilite mucho el cifrado y descifrado de ficheros, el script no está optimizado del todo pero funciona muy bien.

Algunas capturas del script:

#! /bin/bash -x

#By: Dosegundos.wordpress.com

IFS=$\

cd ~
opciones="Descifrar
Cifrar"

while [ 1 ]
do

ms="¿Quieres cifrar o descifrar un fichero?"
result=$(echo $opciones | zenity --list --text="$ms" --column="Acción")

case $result in
"Descifrar")
filter="*.enc"
val=1
;;
"Cifrar")
filter="*"
val=0
;;
*)
zenity --warning --text="Proceso abortado por el usuario"
exit 1
;;
esac

while [ 1 ]
do
archivo=$(zenity --file-selection --file-filter="$filter")
if [ "$(echo $?)" -ne 0 ]
then
msg="No has seleccionado ningún fichero.¿Quieres salir?"
zenity --question --text="$msg"

if [ "$(echo $?)" -eq 0 ];then zenity --info --text="Hasta otra ;)";exit 1;fi
else
nom_orig=$(basename $( echo $archivo ))
zenity --info --text="Fichero seleccionado: $nom_orig"
break
fi
done

while [ 1 ]
do
passpass=$( gksu -p -d "" -m "Contraseña para el fichero $archivo." 2> /dev/null )
if [ $(echo $?) -ne 0 ]
then
zenity --warning --text="Operación cancelada por el usuario."
exit 1
else

if [ "$val" == "1" ]
then
openssl enc -aes-256-cbc -d -in "$archivo" -out "/tmp/${nom_orig%%.enc}" -pass pass:"$passpass"
if [ $(echo $?) -ne 0 ]
then
error=$( echo La contraseña introducida\
no es correcta. Es posible que estés\
intentando desencriptar un fichero no encriptado :P. )
zenity --warning --text="$error"
tx="¿Quieres volver a empezar todo el proceso?"
zenity --question --text="$tx"
if [ $(echo $?) -eq 0 ];then break;fi
mss="¿Quieres volver a introducir la contraseña?"
zenity --question --text="$mss"
if [ $(echo $?) -eq 1 ];then exit 1;fi
else
mv "/tmp/${nom_orig%%.enc}" "${archivo%%.enc}"
rm "$archivo"
msg="
El archivo ${archivo%%.enc} se ha
descifrado correctamente.
No te olvides de volverlo a encriptar!!"
zenity --info --text="$msg"
exit 0
fi
fi
if [ "$val" == "0" ];then
openssl enc -aes-256-cbc -in "$archivo" -out "${archivo}.enc" -pass pass:"$passpass"
if [ "$(echo $?)" -eq 0 ]
then
zenity --info --text="Se ha cifrado correctamente el fichero: $nom_orig"
rm "$archivo"
exit 0
else
zenity --warning --text="No se ha podido cifrar el fichero: $nom_orig"
exit 1
fi
fi
fi
done
done

Muchas manos abrevian el trabajo

El principal problema de un script en Bash es que para poder lanzar otro proceso tenemos que esperar a que finalice el que se está ejecutando, así que el tiempo invertido en una tarea especifica (por ej: La descarga de varios ficheros) puede llevar mucho tiempo, por eso Bash también nos permite iniciar varios procesos a la vez con los comandos bg (Background) y fg (Foreground).

Al lanzar un proceso en segundo plano pierde la posibilidad de leer datos desde la terminal, pero conserva la salida estándar y la salida de errores asociadas a la terminal (si no es que hemos asignado otra salida al comando), por lo que si el proceso escribe un mensaje éste será escrito en la terminal en la que nos encontramos trabajando.

Normalmente al iniciar un proceso en segundo plano se le asigna como salida estándar un fichero especial que se encuentra en /dev/null que descarta todo lo que recibe, por lo que la información enviada a éste fichero no se podrá recuperar de ninguna manera. Enviar la salida estándar al fichero null nos permite ver sólo los errores que han sucedido durante la ejecución del script (o comando).

Poner un proceso en segundo plano

Si estás en la shell y esperas a que un comando acabe puedes presionar ctrl+z y después introducir el comando bg para pasarlo a segundo plano. La combinación de teclas control+z detiene el proceso y con el comando bg enviamos el proceso parado a segundo plano, evitándonos tener que esperar para iniciar otro proceso.

Un ejemplo en el que queremos saber el espacio utilizado en el directorio /var con el comando du:

du -ch /var 2> /dev/null | egrep total &

#La opción c da un total del tamaño y con la opción h nos muestra el tamaño en un formato fácil de leer.

#En 2> /dev/null redireccionamos la salida de error al “agujero negro de linux” (por los errores al intentar acceder a carpetas sin permiso).

De la misma manera que enviamos un comando a trabajar en segundo plano también podemos enviar una función de un script a segundo plano agregando el signo de amperstand & al final de su llamada.

function descarga() {

wget http/loquesea.org/paquet.zip }

descarga &

—Ponemos en marcha otros procesos mientras se descarga el fichero—–>

Para iniciar un script en segundo plano tenemos que añadir el símbolo de amperstand & al final de la orden:

1) bash miscript.sh &

2) ./miscript &

Si no queremos que el script imprima mensajes en la terminal tenemos que redirigir su salida estándar a un archivo o al fichero null, los errores ocurridos durante la ejecución del comando serán escritos en la terminal de trabajo actual.

bash miscript.sh > /dev/null

También podemos enviar la salida de errores a un fichero y evitar que imprima mensajes por la salida estándar:

bash miscript.sh > /dev/null 2>> log.txt

Con las “>>” indicamos que se agregue información al fichero y si no existe lo crea automáticamente, si no mantenemos un registro de errores no podremos comprobar si ha ocurrido un error en un momento especifico, por eso se añade información y no se sobreescribe.

¿Cómo sé que procesos estoy ejecutando en segundo plano?

Para ésta tarea podemos utilizar el comando jobs que nos ofrece la posibilidad de ver el estado en el que se encuentran los procesos que están trabajando en segundo plano.

jobs:

$ sleep 10 &$ jobs

[1]+ Ejecutando sleep 10 &

Si al ejecutar jobs nos aparece un proceso con el estado en “Detenido” tenemos que pasarlo a foreground con el comando fg:

$ read &

$ jobs

[1]+ Detenido read

$ fg 1

El signo de + y delante del número del proceso indica el proceso más reciente y el anterior más reciente.

El comando jobs nos ofrece unas opciones que nos permiten ver el ID del proceso o sólo el ID del proceso sin indicar el estado en el que se encuentra.

La opción -l nos muestra información detallada del estado en el que se encuentran los procesos.

$ read &$ jobs -l

[1]+ 28048 Parado (requiere entrada de terminal) read

Con la opción -p jobs nos muestra el ID de los procesos (PID) sin mostrar el estado en el que encuentran los procesos que están en background.

$ read &$ jobs -p

28048

Otras opciones que pueden ser muy útiles del comando jobs son las siguientes:

  • Opción -s –> Muestra sólo los procesos que están parados.
  • Opción -r –> Procesos que se están ejecutando.

Variables con tipo en Bash.

Por defecto al crear una variable en bash ésta es del tipo string ( aunque después se puede utilizar como si fuese una array sin que nos de ningún problema).
En las primeras shell las variables no podían contener otra cosa que caracteres, pero después se introdujo la posibilidad de asignarles atributos que indiquen de que tipo son, por ejemplo, podemos especificar que tipo de variable vamos a crear, o que nuevo atributo va a tomar una variable ya creada.
Podemos fijar el tipo de variable que vamos a crear con el comando interno declare, la sintaxis del comando es la siguiente:
declare [-aAfFilrtux] [-p] [name[=value] …]
Algo a lo que hay que prestar atención es que para activar un atributo a una variable se procede de un guión , y para desactivar un atributo se coloca el signo + .
Estas son las opciones del comando interno declare.

Opción
Descripción
-a
Indica que es una variable de tipo array.
-F
Muestra sólo el nombre de las funciones.
-f
Imprime el nombre de las funciones y su contenido.
-i
La variable va ser de tipo entero.
-r
Variable de sólo lectura (es como el define de C).
-x
Equivalente al comando export

Si sólo escribimos declare nos mostrará todas las variables de entorno y las funciones junto con su contenido.Las variables que son declaradas dentro de una función con declare son variables locales de la función.
La opción -i nos permite realizar operaciones aritméticas con las variables.
Ejemplo 1 Utilizando variables normales:

numero1=5
numero2=5
resultado=$numero1*$numero2
echo $resultado
<---Salida-->
5*5 <--- Nodevuelve el resultado !!!

Pero si utilizamos las variables tipo entero:

declare -i numero1=5
declare -i numero2=5
declare -i resultado

resultado=$numero1*$numero2
echo $resultado
<---Salida-->
25

Sí que nos ha devuelto el resultado !!

No es necesario que las variables numero1 y numero2 estén declaradas como enteras para que la operación sea exitosa, sino que con declarar a la variable resultado como entera ya es suficiente:

numero1=5
numero2=5
declare -i resultado
resultado=$numero1*$numero2
<---Salida-->
echo $resultado

25 También funciona

Podemos saber a que tipo pertenece una determinada variable con el comando declare -p:

declare -i var1=3
declare -p var1
<---Salida-->
declare -i var="3"

Con la opción -r podemos indicar que una variable sea de sólo lectura, y que desde ese momento no pueda ser modificada o borrada con el comando unset. A la variable a la que queremos asignarle el atributo de sólo lectura debe estar creada y con un valor asignado o asignárselo en el momento de aplicarle el atributo de sólo lectura.

Enviar notificaciones al escritorio de Ubuntu con notify-send

Notify-send es una aplicación que nos puede ayudar a comunicar al usuario eventos importantes que han ocurrido durante la ejecución de uno de nuestros scripts. Notify-send informa al usuario enviando notificaciones que aparecen por la esquina superior derecha del escritorio.

Pantallazo

Si queremos utilizar notify-send tendremos que instalar la libnotify-bin desde la terminal o el gestor de paquetes synaptic (Sistema—>Administración—>Gestor de paquetes synaptic). En la terminal tecleamos el siguiente comando:

sudo apt-get install libnotify-bin

Uso de Notify-send

La manera más simple de utilizar notify-send es la siguiente:

notify-send "Probando Notify-send"

Pantallazo2

También podemos insertar un titulo en la notificación:

# notify-send "Título" "Hola a todos"

Pantallazo3

Si lo encuentras soso, puedes insertar un icono:

notify-send -i appointment-new.png  "Título" "Hola a todos"

Pantallazo4

Si no te funciona el icono debes poner la ruta absoluta de la imagen. Puedes encontrar más iconos en el directorio /usr/share/icons/.

Capturando la salida de un comando

Podemos insertar el resultado de la ejecución de un comando en la misma notificación:

notify-send "Tiempo en funcionamiento:" "$(uptime | cut -d ',' -f1)"

Pantallazo5

Podemos encadenar el notify-send a otro comando, de manera que al acabar de ejecutarse nos envíe una notificación con el operador lógico &&.

wget "http://google.es" && notify-send "Descarga:" "Finalizada"

Pantallazo6

Usar Notify-send en nuestros scripts  es una buena manera de notificar cualquier evento al usuario, de manera de que no tendrá que estar atento a la shell.

Leer una línea especifica de un fichero en Bash

Podemos leer la línea especifica de un fichero usando el siguiente comando:

 head -numero_de_la_línea fichero_de_entrada | tail -1

Supongamos que tenemos un fichero que contiene la fecha de entrega de un trabajo, y otro que contiene el nombre del trabajo y que estos ficheros son muy grandes, por lo que sólo procesaremos el fichero que contiene las fechas de entrega.

#Fechas.txt

dimarts, 21 12 2010, 17:30
divendres, 14 1 2011, 17:35
dimecres, 26 1 2011, 19:00
divendres, 28 1 2011, 14:00
divendres, 4 2 2011, 15:00
divendres, 11 2 2011, 17:00
dilluns, 14 2 2011, 16:00
divendres, 18 2 2011, 17:30
dilluns, 28 2 2011, 16:00
divendres, 4 3 2011, 14:00
dilluns, 14 3 2011, 19:10
#Tareas.txt

A1. Allotjar continguts personals
A2. Llicències Creative Commons
A3. Cercar informació sobre la LOPD
A4. Programa per encriptar arxius amb clau privada
A5. Firmar digitalment i encriptar missatges (Thunderbird+Enigmail)
A1. Comprovar firmes SHA1 i MD5 d'arxius descarregats
A2. Penjar arxius a servidor FTP amb les seves firmes MD5
A3. Encriptar carpetes i particions amb DiskCryptor i TrueCrypt
A4. Configurar les quotes d'usuari i grups amb Ubuntu i Windows.
A5. Analitzador de ports (Port scanner)
A6. Analitzar ports amb l'eina nmap

Ahora vamos a procesar el fichero fechas.txt con el siguiente script, para comprobar que actividades son para hoy.

#! /bin/bash

dia_act=$(date +%d)
mes_act=$(date +%m)
any_act=$(date +%Y)

contador=0

while read linea
do
let contador+=1

fecha=$(echo $linea | cut -d ',' -f2)
#Del formato: día, (dia mes año), hora
#Cortamos el campo que está entre paréntesis
#Del fichero Fechas.txt, ej: dilluns, 14 3 2011, 19:10

fecha_dia=$(echo $fecha | cut -d ' ' -f1)
fecha_mes=$(echo $fecha | cut -d ' ' -f2)
fecha_any=$(echo $fecha | cut -d ' ' -f3)

if [ $fecha_dia -eq $dia_act -a $fecha_mes -eq $mes_act\
-a $fecha_any -eq $any_act ]
then
head -${contador} Tareas.txt | tail -1
#obtenemos la tarea de hoy.
fi

done < Fechas.txt
A6. Analitzar ports amb l'eina nmap

El resultado de la ejecución es la actividad de hoy.