Tomando la extensión en un nombre de archivo

¿Cómo obtengo la extensión de archivo de bash? Esto es lo que probé:

filename=`basename $filepath` fileext=${filename##*.} 

Al hacer eso, puedo obtener la extensión de bz2 de la ruta /dir/subdir/file.bz2, pero tengo un problema con la ruta /dir/subdir/file-1.0.tar.bz2.

Preferiría una solución usando solo bash sin externo programas si es posible.

Para aclarar mi pregunta, estaba creando un script bash para extraer cualquier archivo dado con un solo comando de extract path_to_file. ¿Cómo extraer el archivo está determinado por el script al ver su tipo de compresión o archivo, que podría ser .tar.gz, .gz, .bz2, etc. Creo que esto debería involucrar la manipulación de cadenas, por ejemplo, si obtengo la extensión .gz entonces debería comprobar si tiene la cadena .tar antes de .gz; si es así, la extensión debería ser .tar.gz.

Comentarios

  • file = » /dir/subdir/file-1.0.tar.bz2

; echo $ {file ## *.} imprime ‘ .bz2 ‘ aquí. ¿Cuál es el resultado que ‘ espera?

  • Necesito .tar.bz2
  • Relacionado : Extrae el nombre y la extensión del archivo en Bash .
  • Respuesta

    Si el nombre del archivo es file-1.0.tar.bz2, la extensión es bz2. El método que estás usando para extraer la extensión (fileext=${filename##*.}) es perfectamente válido¹.

    ¿Cómo decides que quieres que la extensión sea tar.bz2 y no bz2 o 0.tar.bz2? Primero debes responder a esta pregunta. Luego, podrás averiguar qué El comando shell coincide con su especificación.

    • Una posible especificación es que las extensiones deben comenzar con una letra. Esta heurística falla en algunas extensiones comunes como 7z, que podría tratarse mejor como un caso especial. Aquí «una implementación de bash / ksh / zsh:

      basename=$filename; fileext= while [[ $basename = ?*.* && ( ${basename##*.} = [A-Za-z]* || ${basename##*.} = 7z ) ]] do fileext=${basename##*.}.$fileext basename=${basename%.*} done fileext=${fileext%.} 

      Para la portabilidad POSIX, debe usar una case declaración para la coincidencia de patrones.

      while case $basename in ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;; *) false;; esac do … 
    • Otra posible especificación es que algunos las extensiones denotan codificaciones e indican que se necesita más eliminación. Aquí «una implementación de bash / ksh / zsh (que requiere shopt -s extglob en bash y setopt ksh_glob en zsh):

      basename=$filename fileext= while [[ $basename = ?*.@(bz2|gz|lzma) ]]; do fileext=${basename##*.}.$fileext basename=${basename%.*} done if [[ $basename = ?*.* ]]; then fileext=${basename##*.}.$fileext basename=${basename%.*} fi fileext=${fileext%.} 

      Tenga en cuenta que esto considera que 0 es una extensión en file-1.0.gz.

    ¹ ${VARIABLE##SUFFIX} y las construcciones relacionadas están en POSIX , por lo que funcionan en cualquier shell de estilo Bourne no antiguo como ash, bash, ksh o zsh.

    Comentarios

    • que deberían ser resuelto, verificando si la cadena antes del último . token es de tipo de archivo, por ejemplo tar, si no es de tipo de archivo como 0 la iteración debería finalizar.
    • @uray: eso funciona en este caso particular, pero ‘ no es una solución general . Considere el ejemplo de Maciej ‘ de .patch.lzma . Un mejor heur istic sería considerar la cadena después del último .: si es ‘ un sufijo de compresión (.7z, .bz2, .gz, …), continúe eliminando.
    • @NoamM ¿Qué estaba mal con la sangría? Está ‘ definitivamente roto después de su edición: el código doblemente anidado tiene la misma sangría que el anidado individualmente.

    Respuesta

    Puede simplificar las cosas simplemente haciendo una coincidencia de patrones en el nombre del archivo en lugar de extraer la extensión dos veces:

    case "$filename" in *.tar.bz2) bunzip_then_untar ;; *.bz2) bunzip_only ;; *.tar.gz) untar_with -z ;; *.tgz) untar_with -z ;; *.gz) gunzip_only ;; *.zip) unzip ;; *.7z) do something ;; *) do nothing ;; esac 

    Comentarios

    • Esta solución es maravillosamente simple.

    Responder

    $ echo "thisfile.txt"|awk -F . "{print $NF}" 

    Comentarios sobre esto aquí: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-shell-script/

    Comentarios

    • no funciona para .tar.gz extensión
    • Bueno, un .tar .gz es en realidad un tar dentro de un archivo gzip, por lo que funciona en el sentido de que elimina una extensión gz de un archivo gzip.

    Respuesta

    Aquí está mi oportunidad: Traducir puntos a nuevas líneas, pasar por tail, obtenga la última línea:

    $> TEXT=123.234.345.456.456.567.678 $> echo $TEXT | tr . \\n | tail -n1 678 

    Respuesta

    Un día he creado esas funciones complicadas:

    # args: string how_many function get_last_letters(){ echo ${1:${#1}-$2:$2}; } function cut_last_letters(){ echo ${1:0:${#1}-$2}; } 

    He encontrado este enfoque sencillo, muy útil en muchos casos, no solo cuando se va acerca de las extensiones.

    Para verificar las extensiones – Es simple y confiable

    ~$ get_last_letters file.bz2 4 .bz2 ~$ get_last_letters file.0.tar.bz2 4 .bz2 

    Para cortar la extensión:

    ~$ cut_last_letters file.0.tar.bz2 4 file.0.tar 

    Para cambiar la extensión:

    ~$ echo $(cut_last_letters file.0.tar.bz2 4).gz file.0.tar.gz 

    O, si le gustan las «funciones útiles:

    ~$ function cut_last_letters_and_add(){ echo ${1:0:${#1}-$2}"$3"; } ~$ cut_last_letters_and_add file.0.tar.bz2 4 .gz file.0.tar.gz 

    PD: si le gustaron esas funciones o las encontró muy útiles, por favor consulte esta publicación 🙂 (y con suerte ponga un comentario).

    Responder

    echo ${filename#$(echo $filename | sed "s/\.[^[:digit:]].*$//g;")} 

    Por ejemplo:

    Comentarios

    • No funciona para todos los casos. Pruebe con ‘ foo.7z ‘
    • Necesita comillas, y mejor use printf en caso de que el nombre del archivo contenga una barra invertida o comience con -: "${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
    • @axel_c : correcto, y ‘ he implementado la misma especificación que Maciej como ejemplo. ¿Qué heurística sugieres que ‘ es mejor que «comienza con una letra»?
    • @Gilles: creo que hay ‘ no es una solución a menos que use una lista precalculada de extensiones conocidas, porque una extensión puede ser cualquier cosa.

    Respuesta

    la respuesta basada en mayúsculas y minúsculas de jackman es bastante buena y portátil, pero si solo desea el nombre del archivo y la extensión en una variable, he encontrado esta solución:

    INPUTFILE="$1" INPUTFILEEXT=$( echo -n "$INPUTFILE" | rev | cut -d"." -f1 | rev ) INPUTFILEEXT=$( echo -n $INPUTFILEEXT | tr "[A-Z]" "[a-z]" ) # force lowercase extension INPUTFILENAME="`echo -n \"$INPUTFILE\" | rev | cut -d"." -f2- | rev`" # fix for files with multiple extensions like "gbamidi-v1.0.tar.gz" INPUTFILEEXT2=$( echo -n "$INPUTFILENAME" | rev | cut -d"." -f1 | rev ) if [ "$INPUTFILEEXT2" = "tar" ]; then # concatenate the extension INPUTFILEEXT="$INPUTFILEEXT2.$INPUTFILEEXT" # update the filename INPUTFILENAME="`echo -n \"$INPUTFILENAME\" | rev | cut -d"." -f2- | rev`" fi 

    Solo funciona con extensiones dobles y la primera debe ser «tar».

    Pero puede cambiar la línea de prueba «tar» con una prueba de longitud de cadena y repetir la corrección varias veces .

    Respuesta

    Lo resolví usando esto:

    filename=`basename $filepath` fileext=${filename##*.} fileext2=${filename%.*} fileext3=${fileext2##*.} if [ "$fileext3" == "tar" ]; then fileext="tar."$fileext fi 

    pero esto solo funciona para tipos de archivo conocidos, en este caso solo tar

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *