Как зациклить файлы в каталоге и изменить путь и добавить суффикс к имени файла

Мне нужно написать скрипт, который запускает мою программу с другими аргументами, но я новичок в Bash. Я начинаю свою программу с:

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

Вот псевдокод для того, что я хочу сделать:

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

Поэтому я действительно озадачен тем, как создать второй аргумент из первого, поэтому он выглядит как dataABCD_Log1.txt и запускает мою программу.

Ответ 1

Несколько примечаний: когда вы используете Data/data1.txt в качестве аргумента, должно ли оно действительно быть /Data/data1.txt (с ведущей косой чертой)? Кроме того, должен ли внешний цикл проверять только файлы .txt или все файлы в /Data? Здесь ответ, предполагающий только файлы /Data/data1.txt и .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

Примечания:

  • /Data/*.txt расширяется до путей текстовых файлов в /Data (включая/Data/part)
  • $( ... ) запускает команду оболочки и вставляет ее вывод в этой точке в командной строке
  • basename somepath .txt выводит базовую часть somepath, с .txt удаленным с конца (например, /Data/file.txtfile)

Если вам нужно запустить MyProgram с Data/file.txt вместо /Data/file.txt, используйте "${filename#/}", чтобы удалить ведущую косую черту. С другой стороны, если это действительно Data не /Data, которое вы хотите отсканировать, просто используйте for filename in Data/*.txt.

Ответ 2

Извините за некроманализацию потока, но всякий раз, когда вы выполняете итерации по файлам с помощью глобинга, рекомендуется избегать углового случая, когда глобус не совпадает (что приводит к расширению переменной цикла до (не совпадающей) строки шаблона глобуса).

Например:

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

Ссылка: Bash Pitfalls

Ответ 3

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

Подстановка name=${file##*/} (расширение параметра оболочки) удаляет ведущий путь до последнего /.

Подстановка base=${name%.txt} удаляет конечный .txt. Это немного сложнее, если расширения могут меняться.

Ответ 4

Вы можете использовать опцию output с нулевым разделением и read для перебора структур каталогов.

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

Так что для вашего случая

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

дополнительно

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

запустит цикл while в текущей области действия скрипта (процесса) и позволит при необходимости использовать вывод find для установки переменных

Ответ 5

Что-то я использую, чтобы избежать лишних строк или проверок:

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

Идея запускает поиск в подоболочке и отправляет ошибки в /dev/null, так что вы никогда не зацикливаете на ошибке об отсутствующих файлах.

Ответ 6

Похоже, вы пытаетесь выполнить файл Windows (.exe) Конечно, вы должны использовать PowerShell. В любом случае на оболочке Linux bash достаточно простой однострочной оболочки.

[/home/$] for filename in /Data/*.txt; do for i in {0..3}; do ./MyProgam.exe  Data/filenameLogs/$filename_log$i.txt; done done

Или в баш

#!/bin/bash

for filename in /Data/*.txt; 
   do
     for i in {0..3}; 
       do ./MyProgam.exe Data/filename.txt Logs/$filename_log$i.txt; 
     done 
 done