Полное руководство: Bash-обработка файлов для автоматизации
Освойте 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, а затем выполнить программу с этими аргументами?
Вы можете создать bash‑скрипт, который будет проходить по файлам в каталоге, генерировать изменённые имена файлов с суффиксами и выполнять вашу программу с этими аргументами. Ниже приведено полное решение, использующее bash‑скриптинг с расширением параметров и вложенными циклами для достижения требуемой функциональности.
Contents
- Basic Script Structure
- Parameter Expansion for Filename Modification
- Complete Working Script
- Script Execution and Testing
- Advanced Variations
- Error Handling and Validation
Basic Script Structure
Чтобы создать скрипт, который будет проходить по файлам и обрабатывать их вашей программой, вам понадобится использовать встроенные возможности bash для итерации по файлам и строковой манипуляции. Базовая структура включает:
- Настройку входного и выходного каталогов
- Создание вложенных циклов (внешний цикл для файлов, внутренний цикл для итераций суффиксов)
- Использование расширения параметров для изменения имён файлов
- Выполнение вашей программы с сгенерированными аргументами
Вот фундаментальный подход:
#!/bin/bash
# Define directories
INPUT_DIR="Data"
OUTPUT_DIR="Logs"
# Create output directory if it doesn't exist
mkdir -p "$OUTPUT_DIR"
# Loop through files in input directory
for file in "$INPUT_DIR"/*.txt; do
# Get filename without extension
filename=$(basename "$file" .txt)
# Inner loop for suffix iterations
for i in {1..4}; do
# Generate log filename with suffix
log_file="${OUTPUT_DIR}/${filename}_Log${i}.txt"
# Execute your program
./MyProgram.exe "$file" "$log_file"
done
done
Parameter Expansion for Filename Modification
Ключ к преобразованию имён файлов — возможности расширения параметров bash. Ниже приведены основные техники:
Basic Parameter Expansion
filename="dataABCD"
echo "${filename}_Log1.txt" # Outputs: dataABCD_Log1.txt
More Advanced Transformations
Вы можете извлекать различные части имён файлов с помощью различных шаблонов расширения параметров:
# Full path handling
full_path="/Data/dataABCD.txt"
basename=$(basename "$full_path") # dataABCD.txt
filename_no_ext="${basename%.*}" # dataABCD
dir_name=$(dirname "$full_path") # /Data
# Extension removal
"${filename%.txt}" # Removes .txt extension
"${filename%%.*}" # Removes all extensions
# Adding suffixes before extension
"${filename%.*}_Log${i}.txt" # dataABCD_Log1.txt
Practical Examples for Your Use Case
# From "dataABCD.txt" to "dataABCD_Log1.txt"
input_file="dataABCD.txt"
output_file="${input_file%.*}_Log1.txt" # dataABCD_Log1.txt
# From "Data/data1.txt" to "Logs/data1_Log1.txt"
input_path="Data/data1.txt"
output_dir="Logs"
filename=$(basename "$input_path" .txt)
output_path="${output_dir}/${filename}_Log1.txt"
Complete Working Script
Ниже приведён полностью готовый скрипт, реализующий ваши требования:
#!/bin/bash
# Script to process files with MyProgram.exe using multiple suffix iterations
# Usage: ./process_files.sh
# Configuration
INPUT_DIR="Data"
OUTPUT_DIR="Logs"
PROGRAM="./MyProgram.exe"
MAX_ITERATIONS=4
# Validate input directory exists
if [ ! -d "$INPUT_DIR" ]; then
echo "Error: Input directory '$INPUT_DIR' does not exist."
exit 1
fi
# Create output directory if it doesn't exist
mkdir -p "$OUTPUT_DIR"
# Check if program exists
if [ ! -x "$PROGRAM" ]; then
echo "Error: Program '$PROGRAM' does not exist or is not executable."
exit 1
fi
echo "Processing files in '$INPUT_DIR' directory..."
# Loop through all .txt files in input directory
for file in "$INPUT_DIR"/*.txt; do
# Skip if no files match (prevents error when glob doesn't match)
[ -e "$file" ] || continue
# Extract filename without extension
filename=$(basename "$file" .txt)
echo "Processing file: $filename"
# Inner loop for suffix iterations (0 to 3, which becomes 1 to 4)
for ((i=1; i<=MAX_ITERATIONS; i++)); do
# Generate log filename with suffix
log_file="${OUTPUT_DIR}/${filename}_Log${i}.txt"
echo " Iteration $i: $PROGRAM $file $log_file"
# Execute your program with both arguments
"$PROGRAM" "$file" "$log_file"
# Optional: Check exit status
if [ $? -ne 0 ]; then
echo "Warning: Program execution failed for $file (iteration $i)"
fi
done
echo "Completed processing: $filename"
echo "------------------------"
done
echo "All files processed successfully!"
Script Execution and Testing
Making the Script Executable
chmod +x process_files.sh
Running the Script
./process_files.sh
Testing with Sample Files
Сначала создайте тестовые файлы и каталоги:
# Create directories
mkdir -p Data Logs
# Create sample data files
echo "Sample data 1" > Data/data1.txt
echo "Sample data 2" > Data/data2.txt
echo "Sample data ABCD" > Data/dataABCD.txt
# Make your program executable (or create a mock for testing)
cat > MyProgram.exe << 'EOF'
#!/bin/bash
echo "Processing: $1"
echo "Log file: $2"
echo "Arguments received: $@"
echo "Program executed successfully" > "$2"
EOF
chmod +x MyProgram.exe
Expected Output
Скрипт выдаст вывод, похожий на:
Processing files in 'Data' directory...
Processing file: data1
Iteration 1: ./MyProgram.exe Data/data1.txt Logs/data1_Log1.txt
Iteration 2: ./MyProgram.exe Data/data1.txt Logs/data1_Log2.txt
Iteration 3: ./MyProgram.exe Data/data1.txt Logs/data1_Log3.txt
Iteration 4: ./MyProgram.exe Data/data1.txt Logs/data1_Log4.txt
Completed processing: data1
------------------------
Processing file: data2
Iteration 1: ./MyProgram.exe Data/data2.txt Logs/data2_Log1.txt
Iteration 2: ./MyProgram.exe Data/data2.txt Logs/data2_Log2.txt
Iteration 3: ./MyProgram.exe Data/data2.txt Logs/data2_Log3.txt
Iteration 4: ./MyProgram.exe Data/data2.txt Logs/data2_Log4.txt
Completed processing: data2
------------------------
Processing file: dataABCD
Iteration 1: ./MyProgram.exe Data/dataABCD.txt Logs/dataABCD_Log1.txt
Iteration 2: ./MyProgram.exe Data/dataABCD.txt Logs/dataABCD_Log2.txt
Iteration 3: ./MyProgram.exe Data/dataABCD.txt Logs/dataABCD_Log3.txt
Iteration 4: ./MyProgram.exe Data/dataABCD.txt Logs/dataABCD_Log4.txt
Completed processing: dataABCD
------------------------
All files processed successfully!
Advanced Variations
Different Suffix Patterns
Если вам нужны другие шаблоны суффиксов, вы можете изменить генерацию имени файла:
# Using different numbering schemes
for ((i=0; i<4; i++)); do
log_file="${OUTPUT_DIR}/${filename}_Log$(($i+1)).txt" # 1-4
# OR
log_file="${OUTPUT_DIR}/${filename}_Log${i}.txt" # 0-3
# OR
log_file="${OUTPUT_DIR}/${filename}_Log$(printf "%02d" $i).txt" # 00-03
done
Recursive Directory Processing
Если нужно обрабатывать файлы во вложенных каталогах рекурсивно:
#!/bin/bash
find "$INPUT_DIR" -name "*.txt" -type f | while read -r file; do
filename=$(basename "$file" .txt)
relative_path="${file#$INPUT_DIR/}"
output_subdir="${OUTPUT_DIR}/$(dirname "$relative_path")"
mkdir -p "$output_subdir"
for ((i=1; i<=4; i++)); do
log_file="${output_subdir}/${filename}_Log${i}.txt"
./MyProgram.exe "$file" "$log_file"
done
done
Parallel Processing
Для ускорения при большом количестве файлов можно использовать параллельную обработку:
#!/bin/bash
export INPUT_DIR OUTPUT_DIR PROGRAM MAX_ITERATIONS
export -f process_file
process_file() {
local file="$1"
filename=$(basename "$file" .txt)
for ((i=1; i<=MAX_ITERATIONS; i++)); do
log_file="${OUTPUT_DIR}/${filename}_Log${i}.txt"
"$PROGRAM" "$file" "$log_file"
done
}
export -f process_file
find "$INPUT_DIR" -name "*.txt" -type f | xargs -n 1 -P 4 -I {} bash -c 'process_file "$@"' _ {}
Error Handling and Validation
Enhanced Error Checking
Ниже приведён более надёжный вариант с обширной обработкой ошибок:
#!/bin/bash
# Enhanced script with error handling and validation
set -euo pipefail # Exit on error, undefined variables, and pipe failures
# Configuration
INPUT_DIR="Data"
OUTPUT_DIR="Logs"
PROGRAM="./MyProgram.exe"
MAX_ITERATIONS=4
# Function to display error and exit
error_exit() {
echo "Error: $1" >&2
exit 1
}
# Validate dependencies
command -v "$PROGRAM" >/dev/null 2>&1 || error_exit "Program '$PROGRAM' not found or not in PATH"
# Validate directories
[ -d "$INPUT_DIR" ] || error_exit "Input directory '$INPUT_DIR' does not exist"
mkdir -p "$OUTPUT_DIR" || error_exit "Cannot create output directory '$OUTPUT_DIR'"
# Check for .txt files
shopt -s nullglob
txt_files=("$INPUT_DIR"/*.txt)
shopt -u nullglob
if [ ${#txt_files[@]} -eq 0 ]; then
error_exit "No .txt files found in '$INPUT_DIR'"
fi
echo "Found ${#txt_files[@]} files to process..."
# Process each file
for file in "${txt_files[@]}"; do
filename=$(basename "$file" .txt)
echo "Processing: $filename"
# Process each iteration
for ((i=1; i<=MAX_ITERATIONS; i++)); do
log_file="${OUTPUT_DIR}/${filename}_Log${i}.txt"
echo " Running iteration $i..."
# Execute with error handling
if ! "$PROGRAM" "$file" "$log_file"; then
echo " Warning: Iteration $i failed for $filename"
# Continue with next iteration or exit
# continue # Remove this line to stop on first error
fi
# Verify log file was created
if [ ! -f "$log_file" ]; then
echo " Warning: Log file '$log_file' was not created"
fi
done
echo " Completed: $filename"
done
echo "All files processed successfully!"
Command Line Arguments
Сделайте ваш скрипт более гибким, принимая аргументы командной строки:
#!/bin/bash
# Script with command line argument support
# Default values
INPUT_DIR="${1:-Data}"
OUTPUT_DIR="${2:-Logs}"
PROGRAM="${3:-./MyProgram.exe}"
MAX_ITERATIONS="${4:-4}"
# Rest of the script using these variables...
Использование:
./process_files.sh Data Logs ./MyProgram.exe 4
Эта всесторонняя реализация предоставляет надёжный shell‑скрипт, который проходит по файлам в каталоге, генерирует изменённые имена файлов с суффиксами и запускает вашу программу с этими аргументами, как указано в ваших требованиях. Скрипт включает надёжную обработку ошибок, проверку и может быть легко настроен под различные сценарии использования.
Sources
- GNU Bash Manual - Parameter Expansion
- Stack Overflow - Bash loop through files in directory
- DigitalOcean - How to Use Wildcards and Globbing in Linux Commands
- GeeksforGeeks - Bash Scripting - For Loop
- IBM Documentation - Advanced Bash-Scripting Guide
Conclusion
- Скрипт успешно проходит по файлам
.txtв каталогеDataи обрабатывает каждый файл несколько раз с разными суффиксами лог‑файлов - Расширение параметров с помощью
${filename%.*}_Log${i}.txtэффективно преобразует имена входных файлов в требуемый формат - Вложенная структура циклов (внешний цикл для файлов, внутренний цикл для итераций) обеспечивает точную функциональность, которую вы запросили в псевдокоде
- Обработка ошибок и проверка гарантируют надёжную работу скрипта даже при отсутствии файлов или каталогов
- Вы можете настроить скрипт, изменив переменную
MAX_ITERATIONS, названия каталогов или логику преобразования имён файлов в соответствии с вашими конкретными требованиями
Для продакшн‑использования рассмотрите добавление логирования, отслеживания прогресса и возможностей параллельной обработки, если вы работаете с большим объёмом файлов. Скрипт предоставляет надёжную основу, которую можно расширять в зависимости от конкретных требований вашей программы и задач обработки файлов.