Мне нужно написать script, который запускает мою программу с разными аргументами, но я новичок в 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 if
end for
Итак, я действительно озадачен тем, как создавать второй аргумент из первого, поэтому он выглядит как dataABCD_Log1.txt и запускает мою программу. Помощь очень ценится.
P.S. Я знаю, что есть аналогичные вопросы, но я ничего не нашел при создании имени моего файла журнала.
Несколько примечаний: когда вы используете 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
.
Извините за некроманизацию потока, но всякий раз, когда вы перебираете файлы с помощью глобинга, рекомендуется избегать углового случая, когда глобус не совпадает (что приводит к расширению переменной цикла до (не совпадающей) строки шаблона глобуса).
Например:
for filename in Data/*.txt; do
[ -e "$filename" ] || continue
# ... rest of the loop body
done
Ссылка: 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
Подстановка name=${file##*/}
(расширение параметра оболочки) удаляет ведущий путь до последнего /
.
Подстановка base=${name%.txt}
удаляет конечный .txt
. Это немного сложнее, если расширения могут меняться.
base=${name%.txt}
вместо base=${base%.txt}
.
Вы можете использовать опцию output с нулевым разделением и read для перебора структур каталогов.
#!/bin/bash
find . -type f -print0 | while read -d $'\0' file;
do echo "$file" ;
done
Так что для вашего случая
#!/bin/bash
find . -maxdepth 1 -type f -print0 | while read -d $'\0' file; do
for ((i=0; i<=3; i++)); do
./MyProgram.exe "$file" 'Logs/'"'basename "$file"'""$i"'.txt'
done
done
$'\0'
- странный способ написания ''
. Вы пропускаете IFS=
и переключатель -r
для read
: ваш оператор чтения должен быть: IFS= read -rd '' file
.
$'\0'
и распределять некоторые точки стека. Собираюсь сделать правки, на которые вы указали. Каковы вредные последствия отсутствия IFS=
попытки echo -e "ok \nok\0" | while read -d '' line; do echo -e "$line"; done
там, кажется, не будет. Также -r я часто вижу значение по умолчанию, но не смог найти пример того, что он предотвращает.
Что-то я использую, чтобы избежать лишних строк или проверок:
#!/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, так что вы никогда не зацикливаете на ошибке об отсутствующих файлах.
Похоже, вы пытаетесь выполнить файл Windows (.exe) Конечно, вы должны использовать PowerShell. В любом случае, в оболочке Linux bash простой однострочной оболочки будет достаточно.
[/home/$] for filename in 'ls /Data/*.txt'; do for i in {0..3}; do ./MyProgam.exe Data/filename Logs/$filename_log$i.txt; done done
Или в баш
#!/bin/bash
for filename in 'ls /Data/*.txt';
do
for i in {0..3};
do ./MyProgam.exe Data/filename.txt Logs/$filename_log$i.txt;
done
done
basename -s
не работает. Я смог запустить скрипт, изменив его наbasename "$filename" .txt
, теперь он работает по назначению. PS Я использую Cygwin, так что может быть дело.basename -s
- это нестандартное расширение - я отредактирую свой ответ, чтобы использовать стандартный синтаксис.