web-dev-qa-db-fra.com

Comment boucler des fichiers dans le répertoire et changer le chemin d'accès et ajouter le suffixe au nom du fichier

Je dois écrire un script qui démarre mon programme avec des arguments différents, mais je ne connais pas encore Bash. Je commence mon programme avec:

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

Voici le pseudocode pour ce que je veux faire:

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

Je suis donc vraiment perplexe de savoir comment créer un deuxième argument à partir du premier. Cela ressemble donc à dataABCD_Log1.txt et au démarrage de mon programme.

487
Dobrobobr

Quelques notes tout d’abord: lorsque vous utilisez Data/data1.txt comme argument, faut-il que ce soit vraiment /Data/data1.txt (avec une barre oblique)? En outre, la boucle externe doit-elle uniquement rechercher les fichiers .txt ou tous les fichiers de/Data? Voici une réponse, en supposant que /Data/data1.txt et les fichiers .txt uniquement:

#!/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

Remarques:

  • /Data/*.txt étend les chemins des fichiers texte dans/Data (y compris le/Data/part)
  • $( ... ) exécute une commande Shell et insère sa sortie à cet endroit de la ligne de commande
  • basename somepath .txt génère la partie de base de somepath, le fichier .txt étant supprimé de la fin (par exemple, /Data/file.txt -> file)

Si vous avez besoin d'exécuter MyProgram avec Data/file.txt au lieu de /Data/file.txt, utilisez "${filename#/}" pour supprimer la barre oblique. D'un autre côté, s'il s'agit vraiment de Data et non de /Data que vous souhaitez analyser, utilisez simplement for filename in Data/*.txt.

640
Gordon Davisson

Désolé de nécromancer le thread, mais chaque fois que vous effectuez une itération sur des fichiers en effectuant un regroupement, il est recommandé d'éviter le coin où le regroupement ne correspond pas (ce qui permet à la variable de boucle de se développer elle-même (sans correspondance).

Par exemple:

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

Référence: pièges Bash

274
Cong Ma
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

La substitution name=${file##*/} ( extension du paramètre Shell ) supprime le chemin précédent jusqu'au dernier /.

La substitution base=${name%.txt} supprime le .txt final. C'est un peu plus délicat si les extensions peuvent varier.

68
Jonathan Leffler

Vous pouvez utiliser l'option de sortie séparée de la recherche null avec read pour parcourir en toute sécurité les structures de répertoires.

#!/bin/bash
find . -type f -print0 | while IFS= read -r -d $'\0' file; 
  do echo "$file" ;
done

Donc pour votre cas

#!/bin/bash
find . -maxdepth 1 -type f  -print0 | while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done

aditionellement

#!/bin/bash
while IFS= read -r -d $'\0' file; do
  for ((i=0; i<=3; i++)); do
    ./MyProgram.exe "$file" 'Logs/'"`basename "$file"`""$i"'.txt'
  done
done < <(find . -maxdepth 1 -type f  -print0)

exécutera la boucle while dans la portée actuelle du script (processus) et autorisera l'utilisation de la sortie de find dans la définition de variables, si nécessaire

5
Arcabard