The Evangeist .INFO

Bash: operadores de comparación

Bash tiene un gran soporte de comparadores de todo tipo que nos permiten hacer comparaciones en los bucles y crear condiciones de todo tipo:

Comparación de enteros (números)

  • -eq
    es igual a

    if [ "$a" -eq "$b" ]
  • -ne
    no es igual a / es distinto a

    if [ "$a" -ne "$b" ]
  • -gt
    es mayor que

    if [ "$a" -gt "$b" ]
  • -ge
    es mayor que o igual a

    if [ "$a" -ge "$b" ]
  • -lt
    es menor que

    if [ "$a" -lt "$b" ]
  • -le
    es menor que o igual a

    if [ "$a" -le "$b" ]
  • <
    es menor que (dentro de doble paréntesis)

    (("$a" < "$b"))
  • <=
    es menor que o igual a (dentro de doble paréntesis)

    (("$a" <= "$b"))
  • >
    es mayor que (dentro de doble paréntesis)

    (("$a" > "$b"))
  • >=
    es mayor que o igual a (dentro de doble paréntesis)

    (("$a" >= "$b"))

Comparación de cadenas

  • =
    es igual a

    if [ "$a" = "$b" ]
  • ==
    es igual a

    if [ "$a" == "$b" ]
  • Nota: Aunque es un sinónimo de = el operador == se comporta diferente cuando se usa dentro de corchetes dobles que simples, por ejemplo:

    [[ $a == z* ]]   # Verdadero si $a empieza con una "z" (expresión regular coincide).
    [[ $a == "z*" ]] # Verdadero si $a es igual a z* (coincide literalmente).
    
    [ $a == z* ]     # Ocurre división de palabras.
    [ "$a" == "z*" ] # Verdadero si $a es igual a z* (coincide literalmente).
  • !=
    no es igual a / Distinto

    if [ "$a" != "$b" ]

    NOTA: este operador usa coincidencia de patrón dentro de doble corchete.

  • <
    es menor que (en orden alfabético ASCII)

    if [[ "$a" < "$b" ]]
    if [ "$a" \< "$b" ]

    Nota: el operador “<” necesita ser escapado dentro de corchetes.

  • >
    es mayor que (en orden alfabético ASCII)

    if [[ "$a" > "$b" ]]
    if [ "$a" \> "$b" ]

    Nota: el operador “>” necesita ser escapado dentro de corchetes.

  • -z
    La cadena está vacía (nulll), tiene longitud cero.

    cadena=''   # Variable de longitud cero (null)
    if [ -z "$String" ]
    then
    echo "\$String está vacía."
    else
    echo "\$String no está vacía."
    fi
  • -n
    cadena no está vacía (contiene algo)
    nota: El operador -n exige que la cadena esté entre comillas entre paréntesis. Aunque el uso son comillas puede funcionar es altamente recomendable usar comillas.

Comparaciones lógicas

  • -a
    Y lógico (and)

    exp1 -a exp2

    devuelve verdadero si ambas exp1 y exp2 son verdaderas.

  • -o
    O lógico (or)

    exp1 -o exp2

    devuelve verdadero si alguna de las expresiones exp1 y exp2 son verdaderas.

Éstos últimos operadores son similares a los operadores de Bash && (and) y || (or) cuando se usan con doble corchete:

[[ condition1 && condition2 ]]

Bash: operaciones aritméticas

Como hacer operaciones aritméticas con enteros dentro de bash que el manual de Bash lo llama Expansión aritmética. Se puede hacer de tres formas:

  1. usando comillas invertidas
    Se utiliza en conjunción con expr y es el menos recomendado ya que usa una aplicación externa. Ejemplos:

    a=`expr 5 % 3`  # módulo
    b=`expr $a + 3` # suma
    area[5]=`expr ${area[11]} + ${area[13]}` # suma de 2 arrays
  2. usando doble paréntesis (( ))
    Es el uso correcto además del let, se puede usar (( )) o $(( )) y dentro se puede usar la variable con o sin $. Ejemplos:

    z=$(($z+3))          #  Variable con $.
    z=$((z+3))          #  Variable sin $.
    (( n += 1 ))  # incrementa 1 a n (mal si se usa $n)
  3. usando let
    Menos usado pero igual de eficiente que (( )), es más al estilo del Basic ;-) . Ejemplos

    let z=z+3
    let "z += 3"
    let "Score[1] += ${Score[0]}"  # con arrays

Bash: funciones

Aunque los scripts en Bash suelen ser pequeños y para hacer cosas puntuales pero muchas veces viene bien tener funciones para evitar repetir código y hacer buenos scripts. Se pueden definir de la siguientes formas (independientemente de si se van a pasar o no parámetros.:

  • function function_name {
    command…
    }
  • function function_name () {
    command…
    }
  • function function_name ()
    {
    command…
    }

Para invocar a una función simplemente se ha de usar su nombre (sin los paréntesis) y en caso de que queramos pasar parámetros uno detrás de otro separados por espacios.

#!/bin/bash

fun () {
   if [ "$1" ]                           # Is parameter #1 zero length?
   then
     echo "Parametro #1 es \"$1\".-"
   fi

   return 0
}

echo "sin parametros."
fun
echo

echo "1 parametro."
fun uno
echo

Dentro de la definición de la función podemos usar los siguientes comandos:

  • swift
    lo que hace es eliminar el primer parámetro y trasladar el segundo al primero y así sucesivamente.
  • return valor
    Termina la función y devuelve un valor.
  • exit valor
    Termina el script con un código de error como valor.

Las variables por defecto son globales por lo que si queremos que una variable sea local tenemos que definirla como local dentro de la función:

func ()
{
  var1=2           # cambia el valor de una variable globalmente
  local var2=23  # define una variable local
}

Descifrar contraseñas Cisco de nivel 7

Como todo el mundo debe saber las contraseñas de Cisco IOS de nivel 7 (password 7) son fácilmente descifrables y hay varias formas de hacerlo, aquí voy a explicar 2, una para Unix y otra para Windows.

Consejo: a ser posible NO usar password de nivel 7 (a menos que sea un entorno controlado y/o local), usar siempre las de nivel 5.

Para Unix (o cualquier sistema operativo que permita ejecutar Perl) pongo a continuación un script en Perl:

#!/usr/bin/perl
#  cpwcrk.pl  -- a small script to crack Cisco's Type 7 password  encryption
#
#
$k='dsfd;kfoA,.iyewrkldJKDHSUB';
for($i=0; $i<length($k); $i++) { $ks[$i] = ord(substr($k, $i, 1)); } 

while (<STDIN>) {
   if(/ord 7 [01]/) {
      chop; $w=$_; s/.* //g; $C = $_;
      printf "$w (decrypted: "; 

      $o=substr($C, 0, 2);
      for ($i=0; $i < (length($C)-1)/2; $i++) { $cs[$i]=hex(substr($C,2*$i,2)); } 

      for ($j=1; $j < $i; $j++) { printf("%c", $ks[$o+$j-1] ^ $cs[$j]); }
      printf ")\n";
   } else {
     printf $_;
   }
}

NOTA: el script no está realizado por mí y desconozco el autor.

Para descifrar una contraseña de nivel 7 podemos hacer 2 cosas:

  1. ejecutar el script e ir tecleando las passwords
    $ ./cpwcrk.pl
    password 7 094F41070A0A1B1318
    password 7 094F41070A0A1B1318 (decrypted: consolas)
    ^C
  2. enviar un echo a la entrada del script.
    $ echo "password 7 094F41070A0A1B1318" | ./cpwcrk.pl
    password 7 094F41070A0A1B1318 (decrypted: consolas)

Para los usuarios Windows (que no dispongan de Perl) la empresa Boson ha realizado una aplicación gratuita que podéis descargar de aquí llamada Get Pass. Una vez instalada y ejecutada la aplicación Get Pass introducimos solo las contraseña y nos devuelve la misma descifrada.
Get Pass en acción

Expresiones regulares

Las expresiones regulares son muy útiles en Unix por eso tener una chuletilla con ellas (o casi todas) siempre está bien.

Posicionamiento
^   Start of line **
\A  Start of string **
$   End of line **
\Z  End of string **
\b  Word boundary **
\B  Not word boundary **
\<  Start of word
\>  End of word
Tipos de caracteres
\c     Control character
\s     White space
\S     Not white space
\d     Digit
\D     Not digit
\w     Word
\W     Not word
\xhh   Hexadecimal character hh
\Oxxx  Octal character xxx
Tipos de caracteres (POSIX)
[:upper:]    Upper case letters
[:lower:]    Lower case letters
[:alpha:]    All letters
[:alnum:]    Digits and letters
[:digit:]    Digits
[:xdigit:]   Hexadecimal digits
[:punct:]    Punctuation
[:blank:]    Space and tab
[:space:]    Blank characters
[:cntrl:]    Control characters
[:graph:]    Printed characters
[:print:]    Printed characters and spaces
[:word:]     Digits, letters and underscore
Assertions
?=    Lookahead assertion **
?!    Negative lookahead **
?<=   Lookbehind assertion **
?!=   Negative lookbehind **
?**
?>    Once-only Subexpression
?()   Condition [if then]
?()|  Condition [if then else]
?#    Comment
Cuantificadores
*        0 or more **
*?       0 or more, ungreedy **
+        1 or more **
+?       1 or more, ungreedy **
?        0 or 1 **
??       0 or 1, ungreedy **
{3}      Exactly 3 **
{3,}     3 or more **
{3,5}    3, 4 or 5 **
{3,5}?   3, 4 or 5, ungreedy **
Rangos (inclusives)
.         Any character except new line (\n) +
(a|b)     a or b **
(...)     Group **
(?:...)   Passive Group **
[abc]     Range (a or b or c) **
[^abc]    Not a or b or c **
[a-q]     Letter between a and q **
[A-Q]     Upper case letter between A and Q **
[0-7]     Digit between 0 and 7 **
\n        nth group/subpattern **
Caracteres Especiales
\            Escape Character **
\n           New line **
\r           Carriage return **
\t           Tab **
\v           Vertical tab **
\f           Form feed **
\a           Alarm
[\b]         Backspace
\e           Escape
\N{name}     Named Character
Sustitución de cadenas
$n   nth non-passive group
$2   "xyz" in /^(abc(xyz))$/
$1   "xyz" in /^(?:abc)(xyz)$/
$`   Before matched string
$'   After matched string
$+   Last matched string
$&   Entire matched string
$_   Entire input string
$$   Literal "$"
Modificadores de Patrones
g   Global match
i   Case-insensitive
m   Multiple lines
s   Treat string as single line
x   Allow comments and white space in pattern
e   Evaluate replacement
U   Ungreedy pattern
Metacaracteres que deben ser escapados
^  $  (  )  <  [  {  \  |  >  .  *  +  ?

NOTA: los que están marcados con ** deberían funcionar en la mayoría de implementaciones de Expresiones Regulares.

Ejemplos de Patrones
([A-Za-z0-9-]+)                            Letras, números y guiones
(\d{1,2}\/\d{1,2}\/\d{4})                  Fecha (21/3/2006)
([^\s]+(?=\.(jpg|gif|png))\.\2)            imagenes jpg, gif o png
(^[1-9]{1}$|^[1-4]{1}[0-9]{1}$|^50$)       Cualquier número del 1 al 50 inclusive
(#?([A-Fa-f0-9]){3}(([A-Fa-f0-9]){3})?)    Código de color hexadecimal válido
((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,15})    Cadena de 8 a 15 caracteres con al menos un letra mayúscula, una minúscula y un dígito (útil para contraseñas)
(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})           Direción de Email
(\<(/?[^\>]+)\>)                           Etiquetas HTML

NOTA: estos son meros ejemplos que deben usarse con cuidado y están puestos como referencia.

La chuleta o cheetsheet original la podéis encontrar en PDF o en PNG en esta web: http://www.addedbytes.com.

Gestión del History en Bash

El histórico (history) tiene varias opciones que podemos cambiar para tener un mejor control del mismo. Aquí vamos a ver algunas opciones para el control y gestión del fichero de histórico (history).

  • Control del tamaño del fichero de histórico:
    Tenemos dos variables de entorno para ello, HISTSIZE y HISTFILESIZE, que indican el tamaño del fichero, por ejemplo:

    HISTSIZE=1000
    HISTFILESIZE=1000

    Con esto hacemos que el tamaño máximo del fichero de histórico sea de 1000 comandos o líneas.
    Si ponemos el tamaño de la variable HISTSIZE a cero hacemos que no se guarde nada en el histórico:

    export HISTSIZE=0
  • Control de duplicados en el histórico:
    En el histórico se van guardando TODOS los comandos que se van introduciendo aunque repitamos 20 veces el mismo, se guardará 20 veces, lo cual es en ocasiones una perdida de espacio, por lo que podemos usar la variable HISTCONTROL para hacer 2 cosas, una eliminar los duplicados consecutivos con ignoredups y dos eliminar los duplicados sean o no consecutivos con erasedups.

    export HISTCONTROL=ignoredups

    o

    export HISTCONTROL=erasedups
  • Donde guardar el histórico:
    por defecto el histórico se guarda en ~/.bash_history pero podemos indicar donde guardarlo con la variable HISTFILE.

    HISTFILE=~/.bitacora

    Cuando en un mismo servidor entran varios administradores que se pasan a root (desde su usuario) y poder controlar y guardar que hace cada uno, podemos tener un histórico por cada uno de ellos de la siguiente forma:

    export HISTFILE=/root/.bash_hist-$(who am i | awk '{print $1}';exit)

    Con esto se guardará en la home de root un fichero de histórico por cada uno de los usuarios que se hayan pasado a root.

  • Añadir fecha y hora al listado del histórico:
    por defecto el History SOLO muestra lo que se ha ejecutado pero no cuando, por eso hay una variable de entorno que nos puede ayudar y sirve para formatear la salida de History, esta variable se llama: HISTTIMEFORMAT.HISTTIMEFORMAT soporta las cadenas de formato de strftime, con lo cual da mucho juego, pero tampoco es para recargar la salida del history.

    Un ejemplo practico del uso de esta variable podría ser:

    Antes del usar la variable HISTTIMEFORMAT:

    $ history
        1  ls /
        2  cd ~
        3  history

    Después de usar la variable:

    $ export HISTTIMEFORMAT='- %F %T - '
    $ history
        1  - 2009-02-14 00:56:49 - ls /
        2  - 2009-02-14 00:56:59 - cd ~
        3  - 2009-02-14 00:57:01 - history
        4  - 2009-02-14 00:58:37 - export HISTTIMEFORMAT='- %F %T - '
        5  - 2009-02-14 00:58:40 - history

    Como se puede ver el histórico no se borra y solo se añade la fecha y la hora (- %F %T -), pero podemos usar cualquier cadena de formato de strftime.

Todas estas variables debemos ponerlas en un fichero donde se activen al arranque que puede ser ~/.bash_profile.

ImageMagick

Varios trucos con ImageMagick:

  • Combinar varias imágenes en una (por ejemplo poner marcas de agua):
    composite -dissolve 15 -tile marca_de_agua.png foto_original.jpg foto_destino.jpg

    dissolve indica el nivel de transparencia, cuanto más bajo más transparente.
    marca_de_agua.png es la imagen con la marca de agua, debe ser con fondo transparente.

  • Cambiar el tamaño de una imagen:
    convert -sample geometría foto_original.jpg foto_destino.jpg

    geometría ver tabla más abajo


Geometría Descripción
scale% Height and width both scaled by specified percentage.
scale-x%xscale-y% Height and width individually scaled by specified percentages. (Only one % symbol needed.)
width Width given, height automagically selected to preserve aspect ratio.
xheight Height given, width automagically selected to preserve aspect ratio.
widthxheight Maximum values of height and width given, aspect ratio preserved.
widthxheight^ Minimum values of width and height given, aspect ratio preserved.
widthxheight! Width and height emphatically given, original aspect ratio ignored.
widthxheight> Change as per widthxheight but only if an image dimension exceeds a specified dimension.
widthxheight< Change dimensions only if both image dimensions exceed specified dimensions.
area@ Resize image to have specified area in pixels. Aspect ratio is preserved.

Editar ficheros .PLIST binarios

Los fichero .PLIST son ficheros XML que se pueden editar con cualquier editor pero a veces pueden estar en formato binario con lo cual hay que convertirlos a formato XML antes de poder editarlos. Leer más »

Bash: secuencias de color ANSI

Para mejorar la salida de los scripts en bash dando un toque de color podemos usar las secuencias de escape de ANSI, por lo que para ello podemos incluir esa función al princicpio de nuestros scripts (o simplemente el código de la función) y ya dispondremos de color en nuestros scripts:

#!/bin/sh

inicializar_colores()
{
# Los colores que empiezan por f_ es color de fondo
# en los modos q_ es quitar el modo
# reset quita cualquier color, modo y pone por defecto la salida

  esc="\033" # si esto no funciona probar "^[" que es ctrl+v+ESC

  negro="${esc}[30m"
  rojo="${esc}[31m"
  verde="${esc}[32m"
  amarillo="${esc}[33m"
  azul="${esc}[34m"
  rosa="${esc}[35m"
  cyan="${esc}[36m"
  blanco="${esc}[37m" Leer más »

Listar ficheros duplicados

Con este mini script podemos listar los ficheros duplicados de cualquier directorio y recursivos:

find DIRECTORIO_PADRE -type f -print0 |  xargs -0 -n1 md5sum | sort --key=1,32 | uniq -w 32 -d --all-repeated=separate | cut -c35-

Explicación:

  • find DIRECTORIO_PADRE -type f -print0
    Busca todos los ficheros (-type f) desde el directorio DIRECTORIO_PADRE y los muestra con el path completo terminado con un carácter null al final(-print0).
  • xargs -0 -n1 md5sum
    pasa cada uno de los fichero encontrados (terminados con null -0 y como máximo 1 -n1) al comando md5sum y devuelve el checksum y el nombre del fichero.
  • sort –key=1,32
    Ordena el listado usando los primeros 32 caracteres que es el checksum del MD5.
  • uniq -w 32 -d –all-repeated=separate
    Muestra SOLO los duplicados (-d), ignorando los únicos, para ello solo compara los 32 primeros caracteres (-w 32) y agrupa los ficheros duplicados en grupos separados por una línea en blanco (–all-repeated=separate)
  • cut -c35-
    Corta los 35 primeros caracteres que el el checsum y espacios, dejando solo el nombre del fichero.