Bash tiene infinidad de opciones, y en los últimos años se ha extendido muchísimo y nos permite hacer cosas muy chulas. Aunque un sistema con tantas opciones como este, es también un poco lioso y difícil de aprender. Por eso en ocasiones viene bien una chuleta para realizar operaciones sencillas que pueden llegar a ser un poco rebuscadas como obtener el nombre de un fichero y su extensión.
Bash, al ser un intérprete de comandos de consola, una de sus principales funciones es trabajar con archivos y cuando queremos utilizar archivos, tenemos que jugar con sus posibles nombres, para ello, deberíamos poder extraer fácilmente la ruta de un archivo, su nombre y separarlo de la extensión. Por ejemplo, si queremos transformar varios archivos de un directorio de jpg a png, o hacer un script que trate de forma los archivos de imagen y de otra los vídeos, o incluso contar cuántos archivos tienen qué extensión...
Para ello, y a modo de autochuleta también, vamos a plantear varios ejemplos en los que se utilizan los modificadores # y % de expansión de parámetros en Bash:
Si ejecutamos esto veremos que primero se mostrará el nombre (archivo) y luego la extensión (txt), aunque, esto deberíamos complicarlo un poco más, y plantearnos preguntas para verificar que funciona en todos los casos, por ejemplo, ¿qué pasaría si el archivo no tiene extensión? En este caso, devolvería el mismo nombre del archivo, y eso puede darnos problemas, para solucionarlo, plantearemos EXTENSION de la siguiente forma:
Ahora bien, ¿qué pasaría si nos pasan un archivo con doble extensión? Como "archivo.tar.bz2″, la variable NOMBRE contendría (archivo.tar) y EXTENSION contendría (bz2). Esto puede ser útil en ciertas ocasiones, por ejemplo, podríamos recorrer todas las posibles extensiones del archivo:
Pero si queremos extraer todas las extensiones juntas del archivo, deberíamos hacer:
Y si queremos el nombre sin ninguna extensión:
Lo que variamos es el número de % y de # que colocamos. En este caso, si utilizamos # estaremos eliminando el prefijo de un patrón, es decir, la parte a la izquierda, y como eliminamos *. queremos decir que eliminamos todo lo que hay delante del punto, ahora si usamos un sólo # estaremos eliminando lo menor posible y si usamos dos, lo mayor posible. De esta forma ${FICHERO#*.} al eliminar poco, nos devuelve todas las extensiones y ${FICHERO#*.}, al encontrar el patrón más grande posible, muestra sólo la última extensión.
De esta forma, nos podemos crear nuestras propias funciones:
Así si hacemos varias llamadas:
$ my_filename archivo
archivo
$ my_filename archivo.tar
archivo
$ my_filename archivo.tar.bz2
archivo
$ my_filename archivo.tar.bz2 1
archivo.tar
$ my_extension archivo
$ my_extension archivo.tar
tar
$ my_extension archivo.tar.bz2
bz2
$ my_extension archivo.tar.bz2 1
tar.bz2
Incluyendo directorios
Si queremos que estas funciones separen también nombres de archivo de directorios tenemos varias opciones. Una de ellas es utilizar basename y dirname, que son dos comandos que muchas veces están disponibles para extraer nombres de archivos y nombres de directorios, así:
$ basename /usr/share/sane/xsane/archivo.tar.bz2
archivo.tar.bz2
$ dirname /usr/share/sane/xsane/archivo.tar.bz2
/usr/share/sane/xsane
Aunque si queremos una solución puramente hecha en Bash, y así evitar hacer llamadas externas y ganar algo de rendimiento podemos utilizar las mismas técnicas anteriores. Es más, primero, vamos a incluir rutas de archivo con las funciones anteriores, a ver qué pasa:
$ my_filename "/usr/share/sane/xsane/archivo.tar.bz2″
/usr/share/sane/xsane/archivo
$ my_extension "/usr/share/sane/xsane/archivo.tar.bz2″
bz2
En principio el nombre incluye la ruta completa, pero la extensión se comporta bien, aunque si el directorio contiene un punto también...
$ my_extension /etc/init.d/lm-sensors
d/lm-sensors
Por tanto, primero vamos a intentar eliminar las rutas, y extraer el nombre de archivo base. Esto lo podemos hacer basándonos en el patrón entre la última barra y el final del nombre, o lo que es lo mismo, eliminando desde el principio del nombre hasta la última barra:
$ echo "${FICHERO##*/}"
y para extraer la ruta solamente, podremos utilizar algo similar a la extensión:
$ $([[ "$FICHERO" = */* ]] & echo "${FICHERO%/*}")
Esto mismo, lo podemos trasladar a las funciones, de la siguiente forma:
Con las que tendremos un acceso más fácil y amigable para nuestros scripts.
Foto principal: Christopher Adams