Recorre todos los archivos con una extensión específica

109
for i in $(ls);do
    if [ $i = '*.java' ];then
        echo "I do something with the file $i"
    fi
done

Quiero recorrer cada archivo en la carpeta actual y verificar si coincide con una extensión específica. El código anterior no funciona, ¿sabes por qué?

AR89
fuente
4
¿Qué hay de for i in $(ls *.java); do echo "do something with file $i"; done?
speakr
no hay forma de arreglar esa declaración if?
AR89
1
Está comparando $icon la cadena literal "* .java"; La expansión del patrón no se realiza aquí.
chepner
Para arreglar la instrucción if como la tiene, use if [[ $i == *.java ]]; then.. (tenga en cuenta el doble [[]] sy sin comillas * .java).
ese otro chico
2
Nols analice - acepte la respuesta de @ chepner
glenn jackman

Respuestas:

201

No se necesitan trucos sofisticados:

for i in *.java; do
    [ -f "$i" ] || break
    ...
done

El guardia asegura que si no hay archivos coincidentes, el bucle saldrá sin intentar procesar un nombre de archivo inexistente *.java. En bash(o shells que admiten algo similar), puede usar la nullglobopción para simplemente ignorar una coincidencia fallida y no ingresar al cuerpo del bucle.

shopt -s nullglob
for i in *.java; do
    ...
done
chepner
fuente
5
La forma más sencilla es añadir otro patrón: for i in *.java *.cpp; do. Si tiene patrones extendidos habilitados bashcon shopt -s extglob, puede escribir for i in *.@(java|cpp); do.
chepner
8
Lo hará si realmente coincide con algún archivo. Debe usarlo shopt -s nullglobpara que un patrón que no coincida se expanda a la secuencia vacía en lugar de ser tratado literalmente.
chepner
1
@zygimantus sí, debería, siempre que el directorio actual sea desde donde se ejecuta el script. Sin embargo, si no se encuentra en el directorio que desea, debe cdir a ese directorio antes de iniciar el forciclo
danielsdesk
1
@puk Depende de sus opciones de shell. De forma predeterminada, un patrón que no coincide se trata como una cadena literal. Puede configurar nullglobque se trate como una secuencia vacía o configurar failglobpara que se trate como un error. (Si establece ambos, failglobtiene prioridad)
Chepner
3
@chepner Podría ser útil para los no expertos tener esto indicado en la respuesta, ya que alguien puede copiarlo y pegarlo y encontrar que no funciona. Un ejemplo perfecto sería alguien que está convirtiendo todos los archivos " .jpg" a " .png" antes de realizar una función crucial
puk
13

la respuesta correcta es @ chepner's

EXT=java
for i in *.${EXT}; do
    ...
done

sin embargo, aquí hay un pequeño truco para verificar si un nombre de archivo tiene extensiones determinadas:

EXT=java
for i in *; do
    if [ "${i}" != "${i%.${EXT}}" ];then
        echo "I do something with the file $i"
    fi
done
umläute
fuente
¿cómo sería con una variable en extlugar de .java?
AR89
13

Agregue subcarpetas de forma recursiva,

for i in `find . -name "*.java" -type f`; do
    echo "$i"
done
luismartingil
fuente
en lugar de find . -name "*.java" -type f -exec echo \{\} \; evitar un análisis incorrecto de la salida defind
umläute
1
Y si no lo hace, al menos debería citar "$i"dentro del ciclo.
tripleee
2
Esto no funcionará si alguno de los archivos tiene espacios en blanco en su nombre.
codeforester
1
@codeforester Tuve exactamente ese problema y lo solucioné usando el método de esta respuesta: askubuntu.com/a/343753/551184
fsinisi90
7

Bucle a través de todos los archivos que terminan con: .img, .bin, .txtsufijo, e imprimir el nombre del archivo:

for i in *.img *.bin *.txt;
do
  echo "$i"
done

O de manera recursiva (encontrar también en todos los subdirectorios):

for i in `find . -type f -name "*.img" -o -name "*.bin" -o -name "*.txt"`;
do
  echo "$i"
done
Benny
fuente
3

Estoy de acuerdo con las otras respuestas con respecto a la forma correcta de recorrer los archivos. Sin embargo, el OP preguntó:

El código anterior no funciona, ¿sabes por qué?

¡Si!

Un artículo excelente ¿Cuál es la diferencia entre prueba, [y [[?] Explica en detalle que, entre otras diferencias, no se puede usar expression matchingo pattern matchingdentro del testcomando (que es la abreviatura de [)

Característica nueva prueba [[prueba anterior [Ejemplo

Coincidencia de patrones = (o ==) (no disponible) [[$ nombre = a *]] || echo "el nombre no comienza con una 'a': $ nombre"

Expresión regular = ~ (no disponible) [[$ (fecha) = ~ ^ Vie \ ... \ 13]] && echo "¡Es viernes 13!"
pareo

Entonces, esta es la razón por la que falla su script. Si el OP está interesado en una respuesta con la [[sintaxis (que tiene la desventaja de no ser compatible con tantas plataformas como el [comando), me complacería editar mi respuesta para incluirla.

EDITAR: ¡Cualquier sugerencia sobre cómo formatear los datos en la respuesta como una tabla sería útil!

user000001
fuente
2

como dice @chepner en su comentario, está comparando $ i con una cadena fija.

Para expandir y rectificar la situación, debe usar [[]] con el operador regex = ~

p.ej:

for i in $(ls);do
    if [[ $i =~ .*\.java$ ]];then
        echo "I want to do something with the file $i"
    fi
done

la expresión regular a la derecha de = ~ se prueba con el valor del operador de la mano izquierda y no se debe citar, (citado no generará errores, pero se comparará con una cadena fija y, por lo tanto, lo más probable es que falle "

pero la respuesta de @chepner anterior usando glob es un mecanismo mucho más eficiente.

peteches
fuente
¿cómo sería con una variable en extlugar de .java?
AR89
1
ack, no se necesita una expresión regular: if [[ $i == *.java ]]o if [[ $i == *.$ext ]]. Pero no analices ls
Glenn Jackman
1

Encontré esta solución bastante útil. Utiliza la -oropción en find:

find . -name \*.tex -or -name "*.png" -or -name "*.pdf"

Se encontrará los archivos con la extensión tex, pngy pdf.

f0nzie
fuente