¿Cómo iterar sobre los archivos en un directorio con Bash?

Necesito escribir un script que inicie mi progtwig con diferentes argumentos, pero soy nuevo en Bash. Comienzo mi progtwig de esta manera:

./MyProgram.exe Data/data1.txt [Logs/data1_Log.txt] .

Aquí está el pseudocódigo para lo que quiero hacer:

 for each filename in /Data do for int i = 0, i = 3, i++ ./MyProgram.exe Data/filename.txt Logs/filename_Log{i}.txt end if end for 

Así que estoy realmente desconcertado sobre cómo crear un segundo argumento a partir del primero, así que parece dataABCD_Log1.txt y comenzar mi progtwig. Se aprecia mucho la ayuda.

PD Sé que hay preguntas similares, pero no encontré nada sobre cómo crear mi nombre de archivo de registro.

Primero un par de notas: cuando usa Data/data1.txt como argumento, ¿debería ser /Data/data1.txt (con una barra diagonal)? Además, ¿debería el bucle externo analizar solo los archivos .txt o todos los archivos en / Datos? Aquí hay una respuesta, asumiendo solo los archivos /Data/data1.txt y .txt:

 #!/bin/bash for filename in /Data/*.txt; do for ((i=0; i<=3; i++)); do ./MyProgram.exe "$filename" "Logs/$(basename "$filename" .txt)_Log$i.txt" done done 

Notas:

  • /Data/*.txt expande a las rutas de los archivos de texto en / Datos ( incluido el / Datos / parte)
  • $( ... ) ejecuta un comando de shell e inserta su salida en ese punto en la línea de comando
  • basename somepath .txt genera la parte base de somepath, con .txt eliminado del final (p /Data/file.txt ej. /Data/file.txt -> file )

Si necesita ejecutar MyProgram con Data/file.txt lugar de /Data/file.txt , use "${filename#/}" para eliminar la barra diagonal. Por otro lado, si realmente se trata de Data no /Data que desea escanear, solo use el for filename in Data/*.txt .

Perdón por necromancer el hilo, pero siempre que itere sobre los archivos mediante globbing, es una buena práctica evitar el caso de esquina donde el globo no coincide (lo que hace que la variable de bucle se expanda hasta la cadena de patrón glob (sin coincidencia)).

Por ejemplo:

 for filename in Data/*.txt; do [ -e "$filename" ] || continue # ... rest of the loop body done 

Referencia: Bash Pitfalls

 for file in Data/*.txt do for ((i = 0; i < 3; i++)) do name=${file##*/} base=${name%.txt} ./MyProgram.exe "$file" Logs/"${base}_Log$i.txt" done done 

El name=${file##*/} sustitución ( expansión del parámetro del shell ) elimina el nombre de ruta principal hasta el último / .

La sustitución base=${name%.txt} elimina el .txt final. Es un poco más complicado si las extensiones pueden variar.

Algo que uso para evitar tener líneas o controles adicionales:

 #!/bin/bash for filename in $(find /Data/*.txt 2> /dev/null); do for ((i=0; i<=3; i++)); do ./MyProgram.exe "$filename" "Logs/$(basename "$filename" .txt)_Log$i.txt" done done 

La idea es ejecutar el hallazgo en una subshell y enviar errores a / dev / null, para que nunca se repita un error sobre archivos perdidos.