Bash脚本使用数组批量转换多格式视频至MKV的故障排查与解决诉求
Hey there! Let's get your script working properly for batch-converting multiple video formats to MKV. The issue you're hitting is all about how Bash expands arrays and wildcards—let's break it down and fix it step by step.
What's Going Wrong
When you use *.${auto_ext[*]}, Bash expands that to something like *.avi mp4 m4p m4v ...—so your for loop treats each of those as a separate filename instead of combining them into wildcard patterns for each format. That's why you're getting errors about files like mp4 not existing (it's trying to open a file literally named mp4, not files ending with .mp4).
Quick note on [*] vs [@]:
"${auto_ext[*]}"joins all array elements into a single string, separated by your system'sIFS(usually a space)."${auto_ext[@]}"treats each array element as a separate, quoted string—this is what you want for looping through individual formats.
The Fix
We need to loop through each format in your array, generate a wildcard pattern for each (like *.avi, *.mp4), and process the matching files. We'll also add a couple of quality-of-life improvements:
- Use
nullglobto avoid errors when a format has no matching files (Bash will just skip that pattern instead of passing*.extas a literal filename). - Redirect error output to
/dev/nullto suppress "file not found" noise for formats with no files.
Updated Script
Here's your modified script with these fixes:
#!/bin/bash # Enable nullglob to skip wildcard patterns that match no files shopt -s nullglob auto_ext=(avi mp4 m4p m4v webm mpg mp2 mpeg mpe mpv ogg wmv mov qt) menu_option_one() { echo "" echo "" echo -n " Enter file extension of file to convert: " read extension for f in *.$extension; do mkvmerge -o "${f%}.mkv" "$f" 2>/dev/null done } menu_option_two() { # Loop through each extension in the array for ext in "${auto_ext[@]}"; do # Process all files with this extension for f in *.$ext; do mkvmerge -o "${f%}.mkv" "$f" 2>/dev/null done done } menu_option_three() { echo "" echo "" echo -n " Enter file extension of file to convert: " read extension for f in *.$extension; do ffmpeg -i "$f" "${f%}.mkv" 2>/dev/null done } menu_option_four() { # Loop through each extension in the array for ext in "${auto_ext[@]}"; do # Process all files with this extension for f in *.$ext; do ffmpeg -i "$f" "${f%}.mkv" 2>/dev/null done done } press_enter() { unset auto_ext unset extension echo "" echo -n " Press Enter to continue... " read clear } incorrect_selection() { echo "Incorrect selection! Try again." } until [ "$selection" = "0" ]; do clear echo "" echo " This file converts video files to mkv in the current directory only... " echo "" echo " 1 - Use mkvmerge (fastest, decent quality)" echo " 2 - Autoconvert all files using mkvmerge" echo " 3 - Use ffmpeg (slowest, best quality)" echo " 4 - Autoconvert all files using ffmpeg" echo " 0 - Exit" echo "" echo -n " Enter selection: " read selection echo "" case $selection in 1 ) clear ; menu_option_one ; press_enter ;; 2 ) clear ; menu_option_two ; press_enter ;; 3 ) clear ; menu_option_three ; press_enter ;; 4 ) clear ; menu_option_four ; press_enter ;; 0 ) clear ; exit ;; * ) clear ; incorrect_selection ; press_enter ;; esac done exit 0
Key Changes Explained
shopt -s nullglob: This tells Bash to remove wildcard patterns that don't match any files, so you won't get errors for formats with no videos in the directory.- Nested loops in
menu_option_twoandmenu_option_four: First we loop through each extension inauto_ext, then for each extension, we loop through all matching files. This ensures we correctly target every file with the specified formats. 2>/dev/null: Redirects error messages (like "file not found" or conversion warnings) to the null device, keeping your output clean.
Now when you run option 2 or 4, the script will process all files matching any format in your auto_ext array—no more manual input required, and you can easily add new formats to the array whenever you need!
内容的提问来源于stack exchange,提问作者BuddyBoy

