¿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
.
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
$ 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/
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
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
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).
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.