Descomponer una ruta en directorios, nombre de archivo y extensión en Bash

Una tarea que puede ser bastante común a la hora de escribir scripts o aplicaciones que trabajen con el sistema de archivos es la necesidad de descomponer una ruta completa del tipo «/directorio/subdirectorio/…/…/archivo.extension» para obtener por separado el nombre de archivo, la extensión y la ruta de directorios. De hecho, fue una de las necesidades que me ha surgido estos días, escribiendo un Shell Script un tanto grande en el que necesitaba obtener el nombre de un archivo de una ruta completa así como la estructura de directorios hasta el mismo.

Dado que me suele gustar, al escribir scripts de cierto tamaño, dividirlos por bloques de código independientes, voy a publicar este como una solución aislada para solventar el problema de descomponer rutas aunque cuando lo volváis a ver en el script que aún no he terminado aparecerá como una función por la comodidad que representan y porque facilitan mucho la lectura del código en scripts de muchas lineas. Sin más preámbulos, vamos manos a la obra:

Dado que este código ahora es independiente, he realizado algunos cambios como que tome la entrada de datos a modo de parametros tras el nombre del script por lo que la ruta a descomponer, deberá ir tras el nombre del script en lo que seria el parámetro $1, es decir, tendriamos que pasárselo de la siguiente manera:

./script.sh /ruta/que/queramos/descomponer/archivo.extension
Por el mismo motivo, lo he dispuesto de forma que la salida del mismo sea la salida estándar mostrando el valor de cada una de las variables que intervienen en el proceso.

También he comentado las lineas relevantes ya que creo que será mejor explicar cada una de las lineas en el mismo punto donde aparece. Así pues, este es el script:

 



#!/bin/bash
# Script para descomponer una ruta completa a un archivo o directorio, obteniendo
# el nombre de archivo y extensión (si existen) y ruta hasta los mismos
for RutaCompleta in $1
do
# Comenzamos extrayendo la parte derecha desde el ultimo caracter "/", es decir, el archivo
NombreArchivo="${RutaCompleta##*/}"
# Longitud de la ruta es el total de caracteres menos el largo del nombre de archivo
LargoRuta="${#RutaCompleta} - ${#NombreArchivo}"
# Extraermos la ruta desde el caracter 0 hasta el caracter final de largo de ruta
RutaSola="${RutaCompleta:0:$LargoRuta}"
# Lo siguiente es extraer nombre de archivo
NombreSolo="${NombreArchivo%.[^.]*}"
# Extension se obtiene eliminando del nombre completo el nombre mas el punto
Extension="${NombreArchivo:${#NombreSolo} + 1}"
# Las 2 lineas anteriores fallan si no hay extension por lo que es necesario comprobar que no
# se de el caso de que haya extension pero no nombre ya que en ese caso la ext seria el nombre
if [[ -z "$NombreSolo" && -n "$Extension" ]]; then
NombreSolo=".$Extension"
Extension=""
fi
done
# Este es el resultado del script
echo
echo "Este es el resultado del script:"
echo
echo La ruta completa es:
echo $RutaCompleta
echo
echo "Ruta.........: \"$RutaSola\""
echo "Nombre.......: \"$NombreSolo\""
echo "Extension....: \"$Extension\""
echo


Si lo ejecutamos, la salida del mismo, según está, sería similar a esto
:

 


$ ./descomp.sh /dir1/subdir1/subdir2/archivo.txt

Este es el resultado del script:

La ruta completa es:
/dir1/subdir1/subdir2/archivo.txt

Ruta………: «/dir1/subdir1/subdir2/»
Nombre…….: «archivo»
Extension….: «txt»


Ahora bien, hay que tener en cuenta que si lo vamos a utilizar como una función llamada desde otro script o simplemente como un bloque de código incrustado entre las lineas, habría que hacerle algunos cambios, pero dado que lo estoy utilizando en otro script que voy a publicar aquí también, no lo explicaré aún ya que es cuestión de tiempo que me toque hacerlo.


 

 

10 Comentarios

  1. Comandos «dirname» y «basename».

  2. A mi me parece más sencillo y claro usar basename y dirname.

  3. Hola iCesofT,
    Lo cierto es que había descartado esos mismos comandos por el mismo motivo que había descartado awk, perl o incluso python, para usarlos en el script, y ciertamente me equivoqué.
    El tema de descartar estos otros métodos de hacerlo era la posibilidad de no estuviesen instalados en algún sistema pero ahí reside mi error ya que no me dio por comprobar a que paquete pertenecían ambos comandos.
    El paquete al que pertenecen es «coreutils», motivo por el cual los podría haber usado perfectamente e incluso habria ahorrado unas lineas de código en el próximo script (Bueno, podría haber ahorrado, no, ahorraré 😉 )

    Todos nos equivocamos y obviamente esta vez me ha tocado 🙂

    No obstante, dependiendo del caso, podría darse la posibilidad de que no se ahorrasen tantas lineas con ellos, me explico:

    Si el fin de las rutas a procesar puede ser tanto un directorio como un archivo y cabe la posibilidad de que no se incluya el carácter «/» al final del último directorio, habria que tratarlo para distinguir si se trata de un directorio o un fichero sin extensión:

    dirname /directorio1/subdirectorio1 –> Devuelve /directorio1
    dirname /directorio1/comando –> Devuelve /directorio1

    basename /directorio1/subdirectorio1 –> Devuelve subdirectorio1 como nombre de archivo

    Ciertamente es un caso bastante concreto que pongo solamente como pequeño apunte (Es el consuelo que me queda, saber que en algún caso sigue siendo útil, después de haber hecho el script :S)

    Sea como sea, gracias por el apunte!

  4. Como ejemplo es muy didáctico, gracias.

  5. wow! Muy interesante todo, la verdad.

  6. estais todos gilipollas o que pasa? el ejemplo es perferto y muy util… aver listillos, como cojones os quedais con el «nombre solo» (sin la extension) de un archivo? por ejemplo $HOME/Escritorio/pruebas/imp.txt quedarte solo con «imp»
    ES IMPOSIBLE…. algun listillo dira: con basename, si le metes basename $HOME/Escritorio/pruebas/imp.txt .txt te lo quita… Hombre claro que lo quita, pero le tienes que decir: quita .txt de forma explicita, y si no sabes si es .txt o .sh o que extension? no se puede…
    asi que es un ejemplo necesario, el saber tratar las cadenas a ese bajo nivel de trocearlas tipo python.

    Conclusion: basename y dirname son inutiles cuando quieres tener por ejemplo el nombre de todos los archivos de un directorio pero sin extension.

  7. A mí también me parece muy útil el script, en absoluto has perdido el tiempo. De hecho, he creado una entrada en mi blog con el script por si le puede ayudar a alguien más.

  8. Me alegro de que lo encuentres útil!! 🙂

  9. Pues a mi tambien me parece muy útil, le he estado dando la vuelta a extraer los nombres y aunque con otros comandos tambien funciona, con basename y dirname no me fué, así que gracias por mi parte.

  10. Aguztíñ IV

    Una duda… he buscado sin éxito qué significa [^.] en tu código, incluso lo he usado [.^] dando resultados diferentes pero no logro entender qué significa y qué hace exactamente, ojalá puedas resolver mi duda, saludos cordiales.

Menciones/Notificaciones

  1. Descomponer una ruta en directorios, nombre de archivo y extensión, en bash - [...] Descomponer una ruta en directorios, nombre de archivo y extensión, en bash www.archivoslog.es/2010/09/descomponer-una-ruta-en-direct...  por alwaro_1 hace 2 segundos [...]