Как зациклить файлы в каталоге и изменить путь и добавить суффикс к имени файла
Мне нужно написать скрипт, который запускает мою программу с другими аргументами, но я новичок в 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.txt
→ file
)
Если вам нужно запустить 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