¿Cómo verificar la extensión de un nombre de archivo en un script bash?


170

Estoy escribiendo un script de compilación nocturno en bash.
Todo está bien y elegante a excepción de un pequeño inconveniente:


#!/bin/bash

for file in "$PATH_TO_SOMEWHERE"; do
      if [ -d $file ]
      then
              # do something directory-ish
      else
              if [ "$file" == "*.txt" ]       #  this is the snag
              then
                     # do something txt-ish
              fi
      fi
done;

Mi problema es determinar la extensión del archivo y luego actuar en consecuencia. Sé que el problema está en la declaración if, que prueba un archivo txt.

¿Cómo puedo determinar si un archivo tiene un sufijo .txt?


Esto se romperá si tiene un archivo con un espacio en su nombre.
jfg956

Además de la respuesta de Paul, puede usar $(dirname $PATH_TO_SOMEWHERE)y $(basename $PATH_TO_SOMEWHERE)dividir en carpeta y directorio y hacer algo directorio-ish y file-ish
McPeppr

Respuestas:


249

Creo que quiere decir "¿Son iguales los últimos cuatro caracteres del archivo $ .txt?" Si es así, puede usar lo siguiente:

if [ ${file: -4} == ".txt" ]

Tenga en cuenta que el espacio entre file:y -4es obligatorio, ya que el modificador ': -' significa algo diferente.


1
para ese fin, también puede cambiar el nombre de command.com a command.txt en una máquina Windows.
Hometoast

9
Si desea especificar una desigualdad, recuerde incluir corchetes adicionales: if [[$ {file: -4}! = ".Txt"]]
Ram Rajamony

44
@RamRajamony ¿Por qué es necesario usar [[cuando se prueba la desigualdad?
PesaEl

Quería señalar que el espacio después del colon es importante. ${var:-4}no es lo mismo que ${var: -4}; el primero (sin espacio) se expandirá como '-4' si var está desarmado el segundo (con un espacio) devuelve los últimos 4 caracteres de var.
pbatey

1
En bash, esto producirá un error "[: ==: operador unario esperado" a menos que ponga comillas alrededor de la primera variable. Entonces en if [ "${file: -4}" == ".txt" ]cambio.
Giles B

264

Hacer

if [ "$file" == "*.txt" ]

Me gusta esto:

if [[ $file == *.txt ]]

Es decir, corchetes dobles y sin comillas.

El lado derecho de ==es un patrón de concha. Si necesita una expresión regular, use =~entonces.


15
No sabía sobre esto. Parece ser un caso especial que el lado derecho de == o! = Se expanda como un patrón de shell. Personalmente, creo que esto es más claro que mi respuesta.
Paul Stephenson

20
Soy nuevo en bash y me tomó un tiempo descubrir cómo usar esto en una ifdeclaración condicional múltiple . Lo estoy compartiendo aquí en caso de que ayude a alguien. if [[ ( $file == *.csv ) || ( $file == *.png ) ]]
joelostblom

66
@cheflo que es bueno para múltiples condiciones en general. En este caso específico, también podría usar if [[ $file =~ .*\.(csv|png) ]]. Es más corto, más claro, más fácil de agregar extensiones adicionales y se puede configurar fácilmente (poniendo "csv | png" en una variable).
jox

44
Puede poner comillas dobles alrededor del archivo. if [[ "$file" == *.txt ]]Si el archivo tiene espacios en su nombre, se requieren comillas dobles.
shawnhcorey

¿Debo usar esta respuesta en lugar de la aceptada?
Freedo

26

Simplemente no puede estar seguro en un sistema Unix, que un archivo .txt realmente es un archivo de texto. Su mejor apuesta es usar "archivo". Quizás intente usar:

file -ib "$file"

Luego, puede usar una lista de tipos MIME para comparar o analizar la primera parte del MIME donde obtiene cosas como "texto", "aplicación", etc.


2
Como file -i...incluye la codificación mime, puede usarfile --mime-type -b ...
Wilf

24

Puede usar el comando "archivo" si realmente desea obtener información sobre el archivo en lugar de confiar en las extensiones.

Si se siente cómodo con el uso de la extensión, puede usar grep para ver si coincide.


Sí, estoy al tanto del filecomando. En realidad, intenté hacer coincidir en función de la salida de dicho comando ... pero fallé horriblemente en estas declaraciones if.

17

También puedes hacer:

   if [ "${FILE##*.}" = "txt" ]; then
       # operation for txt files here
   fi

11
case $FILE in *.txt ) ... ;; esacparecería más robusto e idiomático.
tripleee

11

Similar a 'archivo', use el 'mimetype -b' un poco más simple que funcionará sin importar la extensión del archivo.

if [ $(mimetype -b "$MyFile") == "text/plain" ]
then
  echo "this is a text file"
fi

Editar: es posible que deba instalar libfile-mimeinfo-perl en su sistema si mimetype no está disponible


1
Debe dejar en claro que el script 'mimetype' no está disponible en todos los sistemas.
gilbertpilz

Hecho, agregado libfile-mimeinfo-perl
dargaud

5

La respuesta correcta sobre cómo tomar la extensión disponible en un nombre de archivo en Linux es:

${filename##*\.} 

Ejemplo de imprimir todas las extensiones de archivo en un directorio

for fname in $(find . -maxdepth 1 -type f) # only regular file in the current dir
    do  echo ${fname##*\.} #print extensions 
done

Su respuesta usa una barra invertida doble, pero su ejemplo solo usa una barra invertida simple. Su ejemplo es correcto, su respuesta no lo es.
gilbertpilz

3

Escribí un script bash que analiza el tipo de archivo y luego lo copia en una ubicación, lo uso para ver los videos que he visto en línea desde mi caché de Firefox:

#!/bin/bash
# flvcache script

CACHE=~/.mozilla/firefox/xxxxxxxx.default/Cache
OUTPUTDIR=~/Videos/flvs
MINFILESIZE=2M

for f in `find $CACHE -size +$MINFILESIZE`
do
    a=$(file $f | cut -f2 -d ' ')
    o=$(basename $f)
    if [ "$a" = "Macromedia" ]
        then
            cp "$f" "$OUTPUTDIR/$o"
    fi
done

nautilus  "$OUTPUTDIR"&

Utiliza ideas similares a las presentadas aquí, espero que esto sea útil para alguien.


2

Supongo que '$PATH_TO_SOMEWHERE'es algo como'<directory>/*' .

En este caso, cambiaría el código a:

find <directory> -maxdepth 1 -type d -exec ... \;
find <directory> -maxdepth 1 -type f -name "*.txt" -exec ... \;

Si desea hacer algo más complicado con el directorio y los nombres de los archivos de texto, puede:

find <directory> -maxdepth 1 -type d | while read dir; do echo $dir; ...; done
find <directory> -maxdepth 1 -type f -name "*.txt" | while read txtfile; do echo $txtfile; ...; done

Si tiene espacios en sus nombres de archivo, podría:

find <directory> -maxdepth 1 -type d | xargs ...
find <directory> -maxdepth 1 -type f -name "*.txt" | xargs ...

Estos son excelentes ejemplos de cómo hacer 'bucles' en shell. Los explícitos fory los whilebucles deben reservarse para cuando el cuerpo del bucle debe ser más complejo.
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.