it-swarm-es.tech

Agarrando 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 hacerlo, puedo obtener la extensión de bz2 desde el camino /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 programas externos 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. La secuencia de comandos determina cómo extraer el archivo al ver su tipo de compresión o archivo, que podría ser .tar.gz, .gz, .bz2, etc. Creo que esto debería implicar 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.

36
uray

Si el nombre del archivo es file-1.0.tar.bz2, la extensión es bz2. El método que está utilizando 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 esta pregunta. Luego puede averiguar qué comando de Shell coincide con su especificación.

  • Una posible especificación es que las extensiones deben comenzar con una letra. Esta heurística falla para algunas extensiones comunes como 7z, que podría tratarse mejor como un caso especial. Aquí hay una implementación 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 instrucción case 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 algunas extensiones denotan codificaciones e indican que se necesita una mayor eliminación. Aquí hay una implementación bash/ksh/zsh (que requiere shopt -s extglob bajo bash y setopt ksh_glob bajo zsh):

    basename=$filename
    fileext=
    while [[ $basename = ?*[email protected](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 0 para ser 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.

Puede simplificar las cosas simplemente haciendo coincidir patrones en el nombre de 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
25
glenn jackman
$ 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/

7
Chris

Aquí está mi oportunidad: traduzca puntos a nuevas líneas, canalice a través de tail, obtenga la última línea:

$> TEXT=123.234.345.456.456.567.678
$> echo $TEXT | tr . \\n | tail -n1
678
2
Michael Bar-Sinai
echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}

Por ejemplo:

% echo $filename
2.6.35-zen2.patch.lzma
% echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}
.patch.lzma
0
Maciej Piechotka

Un día he creado esas funciones difíciles:

# 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 trata de extensiones.

Para verificar extensiones - Es simple y confiable

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

Para extensión de corte:

~$ 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 te gustan las "funciones prácticas:

~$ 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ó usadas, consulte esta publicación :) (y esperemos que haga un comentario).

0

la respuesta basada en casos de jackman es bastante buena y portátil, pero si solo desea el nombre de 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.

0
eadmaster