¿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?
.tar.bz2
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 ysetopt 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 enfile-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 ejemplotar
, si no es de tipo de archivo como0
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