Mostrando entradas con la etiqueta linux. Mostrar todas las entradas
Mostrando entradas con la etiqueta linux. Mostrar todas las entradas

martes, 29 de julio de 2014

¿Dónde se ubica este script?

Introducción


Al escribir un script a veces interesa saber en qué directorio está ubicado, por ejemplo, para acceder a algún recurso (un fichero de datos o de configuración asociado.)

Muchos scripts se alojan en un directorio propiedad del usuario y se ejecutan desde el propio directorio con la ruta "./". En tal caso, se podría acceder a un recurso con esa misma ruta. Sin embargo, nada impide ejecutarlo desde otro directorio, o incluso invocarlo desde otro script, con lo que esa forma de referenciar los recursos fallaría.

Aproximaciones


Algunas soluciones burdas, pero efectivas en muchos casos, son tan simples como:
  • Alojar el script y sus recursos en el mismo directorio.
  • Alojar los recursos en determinado directorio fijo y poner toda la ruta cada vez que se referencien desde el script (es mucho mejor poner una variable, claro).
Sin embargo, si deseamos reubicar el script o sus recursos, esas soluciones no sirven o implican al menos editar el script.

Solución


El comando readlink permite obtener el nombre canónico (con su ruta completa) de un fichero.
El comando which permite encontrar los ejecutables que coinciden con el argumento indicado. Por defecto, sólo muestra la ruta completa del primero que se encuentra recorriendo las rutas de la variable de entorno PATH.
En un script la variable $0 corresponde al nombre del propio script.
El comando dirname permite eliminar el sufijo no correspondiente al directorio de una ruta (normalmente el nombre de un fichero junto a su extensión). Es decir, extraer hasta el último directorio de una ruta.

Combinando todo lo anterior, es posible obtener el nombre real del fichero que se está ejecutando y a partir de él el directorio donde está ubicado:

MYSELF="$(which $(readlink -f ${0}))"
MY_PATH="$(dirname $(which $(readlink -f ${0})))"

La variable MYSELF contendrá el nombre canónico del script y la variable MY_PATH el directorio donde está alojado.

Ejemplo


Si quieres probar que funciona en varios casos, puedes hacer lo siguiente:

#!/bin/bash

MYSELF="$(which $(readlink -f ${0}))"
MY_PATH="$(dirname $(which $(readlink -f ${0})))"
echo "\$0=${0}"
echo "\$MYSELF=   ${MYSELF}"
echo "\$MY_PATH=  ${MY_PATH}"

  • Crear un script con las líneas que anteriores llamado "cat".
  • Poner el script en un directorio cualquiera de tu home.
  • Ir a /tmp/ y crear un enlace simbólico al script ejecutando
    $ ln -s /home/_your_user/.../cat
  • Alterar la variable PATH en la sesión actual:
    $ PATH=/tmp/:$PATH
  • Acceder a cualquier directorio y ejecutar
    $ cat something
En lugar de ejecutarse el comando cat del sistema, se ejecutará el script, mostrando su nombre canónico y el directorio donde está, aunque en realidad, se esté ejecutando el enlace simbólico que se puso en el directorio /tmp/.

Conclusión


Probablemente haya alguna solución mejor, más elegante o más precisa, pero hasta la fecha, a mi me ha bastado con esta.

Enlaces


Enlaces de interés relacionados con este artículo:

(Actualizado 29/07/2014)

domingo, 29 de junio de 2014

Cómo mejorar scripts en bash

Introducción


Si has escrito scripts en algún intérprete de comandos como bash probablemente hayas tenido errores porque al haber escrito mal el nombre de una variable, el intérprete la define en ese momento vacía.

También se te habrá dado el caso de querer interrumpir la ejecución desde que algún comando falle, ya que por defecto, la ejecución de un script no se interrumpe aunque falle algún comando. Por ejemplo, si cambias de directorio (cd) y luego borras ficheros (rm), si falla el comando cd el resultado del comando rm puede ser desastroso.

Parámetros de configuración


El bash se puede configurar para abortar la ejecución de un script si se utiliza una variable que no ha sido inicializada:

#Expresiones equivalentes
set -o nounset
set -u

Esa opción es muy útil y puede evitarte muchos accidentes y quebraderos de cabeza.

También se puede configurar que se interrumpa la ejecución del script si falla algún comando:

#Expresiones equivalentes
set -o errexit
set -e

OJO, se considera fallar que el comando devuelva un valor distinto de 0.

Hay más parámetros que te pueden interesar como "pipefail" por ejemplo.

Ejemplos


Con este pequeño script, activando o desactivando los parámetros anteriores, puedes comprobar tú mismo como cambia el comportamiento del bash:

set -o nounset
set -o errexit
###############################################################################

#echo "Using mode: nounset"
echo "This command is executed: OK."
echo "This command fails because this variable has not been initialized <${VAR_UNDEF}>."
echo "This command is not executed because previous command failed."

#echo "Using mode: errexit"
echo "This command is executed: OK."
#Next command fails because the file doesn't exist.
cat "/tmp/unexistent.file"
echo "This command is not executed because previous command failed."

Si quieres hacer pruebas on-line, puedes usar este enlace:


Observaciones


Respecto al modo "errexit" hay mucha controversia (más abajo hay algunos enlaces si te interesa el tema). Hay que tener muy en cuenta que se aborta cuando un comando devuelve algo distinto de 0, lo cual no tiene por qué significar necesariamente que haya fallado. De hecho, hay comandos que intencionadamente devuelven valores distintos de 0 sin tratarse de errores.

Otra cosa que se comenta por ahí, es que algunas de las características (como "pipefail") van cambiando de una versión de bash a otra.
 

Conclusión


En mi humilde opinión, el modo "nounset" debería ser el comportamiento por defecto, pero bueno, para gustos los colores.

Yo considero que el modo "errexit" es una herramienta muy útil que el programador debe saber cuando usar y cuando no.

A veces interesa buscar si el comportamiento de alguna herramienta es configurable, porque muchas veces, el comportamiento por defecto no nos conviene.

Enlaces


Enlaces de interés relacionados con este artículo:

(Actualizado 29/06/2014)

domingo, 27 de abril de 2014

¿Cómo obtener la ocurrencia más corta usando expresiones regulares?

Introducción


Alguna vez habrás usado expresiones regulares (regular expression), ya sea programando, en la línea de comandos o en algún editor de texto.

¿No te ha pasado que al usar algún comodín de repetición como el * en vez de obtener la ocurrencia más corta obtienes la más larga (que contiene ocurrencias más cortas)?

Por ejemplo, al analizar la cadena XML:

<a>1111</a><a>2222</a><a>3333</a><a>4444</a>

con la expresión regular siguiente para locazizar los elementos (tags) a:

<a>.*</a>

obtienes toda la cadena en vez de únicamente el primer elemento <a>1111</a>.

Ups, hay ambigüedad: encajan varios trozos de la cadena y además encaja toda la cadena. ¿Qué resultado se elige? 

Greediness vs Laziness


El problema es que al diseñar un motor de expresiones regulares, hay que resolver la ambigüedad. Hay dos comportamientos:
  1. Greediness (avaricia, codicia): el comodín se expande consumiento el máximo número de caracteres posible. En la documentación se suele denominar modo greedy.
  2. Laziness (pereza): el comodín se expande consumiendo el mínimo número de caracteres posible. En la documentación se suele denominar modo lazy, non-greedy o reluctant.
Algunos motores de búsqueda permiten configurar el comportamiento, otros no.

Si te interesa mucho el tema, en estos enlaces se explica (en inglés) muy bien como funcionan los motores de expresiones regulares y sus comportamientos:

Cómo capturar la ocurrencia más corta (non-greedy)


Algunos motores de expresiones regulares han resuelto la ambigüedad utilizando operadores diferentes para los modos greedy y non-greedy.

El operador para 0 o N ocurrencias más conocido es el greedy * mientras que el non-greedy suele ser una variación suya *? en la mayoría de los casos.

A continuación hay ejemplos del operador non-greedy en varios entornos. Para ello, se utiliza la cadena y la expresión regular de la introducción.

En lenguajes interpretados se muestra la lista de ocurrencias, por ser fácil y muy ilustrativo.

En lenguajes compilados, por simplicidad, se reemplazan todas las ocurencias por la palabra Replaced, de modo que mirando el resultado se sabe si el comportamiento fue greedy (Replaced aparecerá sólo una vez) o non-greedy (Replaced aparecerá varias veces).

grep (hay que activar el modo Perl)
echo "<a>1111</a><a>2222</a><a>3333</a><a>4444</a>" | grep -P -o "<a>.*?</a>"

Vim (este caso difiere mucho del resto)
<a>.\{-}<\/a>
Para detectar tags cuyo contenido incluya saltos de línea:
<a>\_.\{-}<\/a>

Perl (ejecutable en línea de comandos)
perl -e 'my @matches= "<a>1111</a><a>2222</a><a>3333</a><a>4444</a>" =~ /<a>.*?<\/a>/g; print (join("\n", @matches), "\n");'

Ruby (ejecutable en línea de comandos)
ruby -e 'puts "<a>1111</a><a>2222</a><a>3333</a><a>4444</a>".scan( /<a>.*?<\/a>/ ).to_a'

PostgreSQL
SELECT regexp_matches('<a>1111</a><a>2222</a><a>3333</a><a>4444</a>', '<a>.*?<\/a>', 'g');

PHP (ejecutable en línea de comandos)
php -r '$matches= array(); $search= preg_match_all("/<a>.*?<\/a>/", "<a>1111</a><a>2222</a><a>3333</a><a>4444</a>", $matches); print_r($matches);'

Java
"<a>1111</a><a>2222</a><a>3333</a><a>4444</a>".replaceAll("<a>.*?</a>", "Replaced")

Javascript
function doReplace() {
  var str= "<a>1111</a><a>2222</a><a>3333</a><a>4444</a>"
  str= str.replace(new RegExp("<a>.*?</a>", "g"), "Replaced");
  alert(str);
}

C++
Para menejar expresiones regulares en C++ haría falta un compilador que soporte por completo C++11 (GCC 4.9) o bibliotecas alternativa como boost o clang.

sed (este comando siempre es greedy)
http://stackoverflow.com/questions/1103149/non-greedy-regex-matching-in-sed

bash (sus funciones/operadores de expresiones regulares como =~ siempre son greedy)
Al parecer, las expresiones regulares POSIX sólo soportan modo greedy.
http://stackoverflow.com/questions/18738576/regex-in-bash-expression

Prúebalo tu mismo


Aquí hay enlaces a varias paǵinas web para probar algunas de las expresiones anteriores directamente on-line:

Enlaces


Enlaces de interés relacionados con este artículo:


(Actualizado 10/07/2014)


miércoles, 31 de julio de 2013

Crónicas del GNOME y el teclado numérico

Te podría pasar a tí



Un buen día estás ahí, sentado delante de un ordenador con alguna distribución de Linux (bueno, GNU/Linux) y usando GNOME.

Tras varias horas tecleando a gran velocidad, cambiando de ventana para comparar resultados, copiando, pegando, de repente...

¡¡Vaya, resulta que no puedes usar el teclado numérico, el asterisco y la barra no funcionan!!

Y te preguntas:
- Ummm, ¿pero si la lucecita de "Bloq Num" está encendida, qué es lo que ocurre?

Le das repetidamente a la tecla de "Bloq Num", la lucecita se apaga y se enciende, se apaga y se enciende, y todo sigue igual:
- ¡¿eh? qué raro!

Acto seguido, descubres que otras teclas como el cursor tampoco funcionan y que otras hacen cosas raras, muy raras, pero que muy muy raras:
- ¡¡¡Vaya, el Más(+) abre el menú contextual de la aplicación, ¿pero que co·ooo...?!!!
- ¡¡¡Y el teclado numérico mueve el cursor del ratón!!!


Visiblemente enfado tratas de cerrar la aplicación con ALT+F4 y ¡hala!, resulta que tampoco funciona:
- Pe-pe-pero, ¡¿qué co·ones pasa?!
- ¡Pa' que luego digan que Linux no se cuelga...! ¡JA!

Y te dices a tí mismo:
- ¡Chacho, no voy a reiniciar todo el ordenador por esto...!
- ¡Ya lo tengo!

Y decides reiniciar el servidor X.

Pero para tu sorpresa, ¡anda todo sigue igual!
Ja ja ja (en la lejanía oyes como el universo se ríe de tí).

- ¡A la mier··!
Decides reiniciar el sistema mientras respiras aliviado pensando:
- !me ca·o en..., a lo que hay que llegar!

Pero para más inri te das cuenta de que, incluso habiendo reiniciado, todo sigue igual.
- ¡ESTO ES EL COLMO!

Harto de propinar improperios al ordenador:
Desenchufas y enchufas el teclado...

Y pruebas otro teclado...

Y desenchufas y enchufas el ratón...

Y pruebas otro ratón...

Y... pruebas esto...

Y... pruebas lo otro...

Y...

Hasta que empiezas a pensar:
- ¿Umm tendré algún disco de instalación de Linux por aquí?


Pero a ver, animalito de Dios, ¿por qué no empezaste por buscar en Internet?

¿Qué está pasando y cómo lo arreglo?



Lo que ha pasado es que accidentalmente cambiaste el modo de funcionamiento del teclado con la combinación:

SHIFT + "Bloq Num"

Lo único que hay que hacer es volver a pulsar esa combinación para volver al modo de funcionamiento inicial.

Esta característica se ha comprobado en GNOME 2.22.3 y 2.30.2, aunque no descarto que esté presente en otras versiones.

Recuerda: por malo que parezca, sonríe, siempre podría ser peor.


Moraleja: En GNOME, NI SE TE OCURRA PULSAR Shift + "Bloq Num", pero si lo haces, hazlo un número par de veces.



Enlaces de interés


  • http://www.cyberciti.biz/faq/linux-numeric-keypad-disabled-mouse-keysonly-works/
  • http://ubuntuforums.org/showthread.php?t=949500


(Actualizado 31/07/2013)