Recursivamente busca archivos con una extensión específica

437

Estoy tratando de encontrar todos los archivos con una extensión específica en un directorio y sus subdirectorios con mi bash (última versión de Ubuntu LTS).

Esto es lo que está escrito en un archivo de script:

#!/bin/bash

directory="/home/flip/Desktop"
suffix="in"

browsefolders ()
  for i in "$1"/*; 
  do
    echo "dir :$directory"
    echo "filename: $i"
    #   echo ${i#*.}
    extension=`echo "$i" | cut -d'.' -f2`
    echo "Erweiterung $extension"
    if     [ -f "$i" ]; then        

        if [ $extension == $suffix ]; then
            echo "$i ends with $in"

        else
            echo "$i does NOT end with $in"
        fi
    elif [ -d "$i" ]; then  
    browsefolders "$i"
    fi
  done
}
browsefolders  "$directory"

Desafortunadamente, cuando comienzo este script en la terminal, dice:

[: 29: in: unexpected operator

(con en $extensionlugar de 'in')

¿Qué está pasando aquí, dónde está el error? Pero este corsé rizado

dar la vuelta
fuente
2
El error es de un faltante '{'
shrewmouse

Respuestas:

750
find $directory -type f -name "*.in"

es un poco más corto que todo eso (y más seguro: trata con espacios en blanco en los nombres de archivos y directorios).

Probablemente, su secuencia de comandos esté fallando para las entradas que no tienen un .nombre, quedando $extensionvacías.

Estera
fuente
16
Sí, findes recursivo por defecto. puede limitar las profundidades si lo desea (consulte la página de manual).
Mat
1
Me gustaría pasar todos los archivos encontrados como argumentos a un archivo jar. ¿Cómo se puede realizar esto?
voltear
8
@flip: esa es una pregunta diferente. Publique una nueva pregunta, detallando exactamente lo que le gustaría hacer y lo que ha intentado hasta ahora.
Mat
Una pequeña corrección: use '* .in' o \ *. In en lugar de "* .in" porque las comillas dobles no impiden la expansión del shell. Es decir, su script no funcionará correctamente si hay un archivo con extensión .in en el directorio actual.
Shnatsel
44
@Shnatsel: las comillas dobles evitan la expansión del shell. Pruébalo.
Mat
188
find {directory} -type f -name '*.extension'

Ejemplo: para buscar todos los csvarchivos en el directorio actual y sus subdirectorios, use:

find . -type f -name '*.csv'
Mohammad AlQanneh
fuente
60

La sintaxis que uso es un poco diferente de lo que sugirió @Matt:

find $directory -type f -name \*.in

(Es una pulsación menos).

Scott C Wilson
fuente
1
El script de Matt tampoco funcionará si hay un archivo con extensión .in en el directorio actual, mientras que el tuyo aún funcionaría. Ver stackoverflow.com/questions/5927369/…
Shnatsel
44
@Shnatsel este comentario (y por lo tanto el tuyo) es simplemente incorrecto.
gniourf_gniourf
1
@gniourf_gniourf Debe proporcionar alguna referencia para su declaración, de lo contrario uno podría simplemente argumentar: "No, está equivocado". Pero de hecho tiene razón: gnu.org/software/bash/manual/html_node/Double-Quotes.html
Murmel
@ user1885518: Creo que debería ser el tipo que afirma que el script no funciona quien debería proporcionar algunos ejemplos en los que el script falla. Eso es lo que hago cuando dejo comentarios donde hay scripts rotos: generalmente se trata de citas y nombres de archivos que contienen espacios, líneas nuevas, globos, etc., y explico específicamente por qué está roto.
gniourf_gniourf
2
Proporcionar referencia es siempre una buena manera en una discusión, no depende de quién fue el primero. Él debería, tú deberías.
Murmel
14

Sin usar find:

du -a $directory | awk '{print $2}' | grep '\.in$'
rtrn
fuente
3
El grepno es realmente necesario aquí. awktiene expresiones regulares y podría limitar su salida a valores que coincidan con un patrón.
Kenster
Este método es extremadamente útil si estás pasando por cientos de terabytes. El comando Buscar tarda demasiado en procesarse. Esto comienza de inmediato.
Protonova
1
awk|grepEs un antipatrón. Deja que awk haga el grepping.
Jens
10
  1. Hay una { desaparecido despuésbrowsefolders ()
  2. Todas $in debe ser$suffix
  3. La línea con cutte lleva solo a la parte media front.middle.extension. Debes leer tu manual de shell ${varname%%pattern}y amigos.

Supongo que haces esto como un ejercicio de scripting de shell, de lo contrario, la findsolución ya propuesta es el camino a seguir.

Para verificar la sintaxis de shell adecuada, sin ejecutar un script, use sh -n scriptname.

Jens
fuente
10
find "$PWD" -type f -name "*.in"
kip2
fuente
7

Aunque el uso de findcomandos puede ser útil aquí, el shell en sí mismo proporciona opciones para cumplir este requisito sin herramientas de terceros. losbash shell proporciona una opción de soporte global extendido mediante la cual puede obtener los nombres de archivo en rutas recursivas que coincidan con las extensiones que desee.

La opción extendida es la extglobque debe configurarse utilizando la shoptopción que se muestra a continuación. Las opciones están habilitadas con el -ssoporte y deshabilitadas con la -ubandera. Además, podría usar un par de opciones más, es decir, nullgloben el que un globo incomparable se elimina por completo, reemplazado por un conjunto de cero palabras. Y globstareso permite recurrir a través de todos los directorios.

shopt -s extglob nullglob globstar

Ahora todo lo que necesita hacer es formar la expresión global para incluir los archivos de una determinada extensión, que puede hacer como se muestra a continuación. Usamos una matriz para llenar los resultados globales porque cuando se citan correctamente y se expanden, los nombres de archivo con caracteres especiales permanecerían intactos y no se romperían debido a la división de palabras por el shell.

Por ejemplo, para enumerar todos los *.csvarchivos en las rutas recursivas

fileList=(**/*.csv)

La opción **es recurrir a través de las subcarpetas y *.csves una expansión global para incluir cualquier archivo de las extensiones mencionadas. Ahora para imprimir los archivos reales, solo haz

printf '%s\n' "${fileList[@]}"

Usar una matriz y hacer una expansión entre comillas adecuada es la forma correcta cuando se usa en scripts de shell, pero para uso interactivo, simplemente puede usar lscon la expresión global como

ls -1 -- **/*.csv

Esto podría muy bien expandirse para que coincida con varios archivos, es decir, el archivo que termina con una extensión múltiple (es decir, similar a agregar varios indicadores en el findcomando). Por ejemplo, considere un caso de necesidad de obtener todos los archivos de imágenes recursivas, es decir, de extensiones *.gif, *.pngy *.jpgtodo lo que necesita es

ls -1 -- **/+(*.jpg|*.gif|*.png)

Esto podría muy bien expandirse para tener resultados negativos también. Con la misma sintaxis, uno podría usar los resultados del globo para excluir archivos de cierto tipo. Suponga que desea excluir los nombres de archivo con las extensiones anteriores, podría hacer

excludeResults=()
excludeResults=(**/!(*.jpg|*.gif|*.png))
printf '%s\n' "${excludeResults[@]}"

La construcción !()es una operación negativa para no incluir ninguna de las extensiones de archivo enumeradas en el interior y |es un operador de alternancia tal como se usa en la biblioteca de Expresiones regulares extendidas para hacer una coincidencia OR de los globos.

Tenga en cuenta que este soporte global extendido no está disponible en el shell POSIX bourne y es puramente específico de las versiones recientes de bash. Entonces, si está considerando la portabilidad de los scripts que se ejecutan en POSIX y bashshells, esta opción no sería la correcta.

Inian
fuente
6

Para encontrar todos los pom.xmlarchivos en su directorio actual e imprimirlos, puede usar:

find . -name 'pom.xml' -print
Bharat Yadav
fuente
1
find $directory -type f -name "*.in"|grep $substring
Sergiu
fuente
0
for file in "${LOCATION_VAR}"/*.zip
do
  echo "$file"
done 
Avinash Kumar Mishra
fuente
1
Si bien este código puede responder la pregunta, proporcionar un contexto adicional con respecto a por qué y / o cómo responde la pregunta mejora su valor a largo plazo.
rollstuhlfahrer