Home > database >  I'm trying to batch convert different video files into mkv using arrays in a bash script using
I'm trying to batch convert different video files into mkv using arrays in a bash script using

Time:12-31

I have a script that I use to convert my video files in a bash script from multiple video format extensions using either ffmpeg or mkvmerge. I am able to do a batch convert of a single extension in the script with no problem by entering the extension using read extension, but I am trying to figure out how to do this with an array of the video extensions so I don't have to enter each extension that is to be converted. I'm figuring I can add more extensions to the array as is needed.

I am completely new to using arrays in a bash file so this is all new territory for me.

What I am trying to do is make it so that I can place the script into the directory with the videos to convert, run the script and it will convert all the videos it finds, from those in the array list, to mkv. I am trying to do this using an array but it's not quite going right. It works fine for the first item in the list, avi, but when it gets to the second and on to the rest of the extensions in the array it will not place the wildcard filename into the script, therefore not process the filenames. Here is what I have currently:

#!/bin/bash

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";done
}

menu_option_two() {
        for f in *.${auto_ext[*]}; do mkvmerge -o "${f%}.mkv" "$f";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";done 
}

menu_option_four() {
    for f in *.${auto_ext[*]}; do ffmpeg -i "$f" "${f%}.mkv";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

The output I am getting for mkvmerge when having .mp4, .webm, and .m4p in the directory is:

mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file '*.avi' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'mp4' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'm4p' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'm4v' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'webm' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'mpg' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'mp2' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'mpeg' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'mpe' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'mpv' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'ogg' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'wmv' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'mov' could not be opened for reading: open file error.
mkvmerge v45.0.0 ('Heaven in Pennies') 64-bit
Error: The file 'qt' could not be opened for reading: open file error.

ffmpeg just says *.: No such file or directory

I have tried using both the wildcards array[*] and array[@] in the for loop but I'm getting the same results. Honestly, I'm not real sure what the difference is between the two. I have searched here and in other forums and the questions all focus on converting only one video type to another but not finding and converting multiple formats at the same time. I think I may be missing a step in the processing of the array, but that's just a guess.

Since the output is putting an error for the extensions not found, I am thinking I will also have direct the errors to a null somewhere in the script but I'm not sure where.

CodePudding user response:

In the menu_option_two() function, the statement

for f in *.${auto_ext[*]}

is expanded as:

for f in *.avi mp4 m4p m4v webm ...

assiging f to each of globbed files of *.avi (if exist), mp4, m4p, ...

You will instead need to create double loop as:

for ext in "${auto_ext[@]}"; do
    for f in *."$ext"; do
        if [[ -f $f ]]; then
            mkvmerge -o "${f%$ext}mkv" "$f"
        fi
    done
done

The if [[ -f $f ]] condition is required to avoid the variable f is assigned to literal *.avi, e.g., when the type glob fails. (Alternatively you can set nullglob shopt option.)

CodePudding user response:

What I ended up doing is, as Ishiano suggested, a double loop, but with a little bit different twist.

for ext in ${auto_ext_2[@]}; do
    count_file=`ls -1 *.${ext} 2>/dev/null | wc -l`
    if [ $count_file != 0 ]
        then 
            for f in *.$ext; do
                ffmpeg -i "$f" "${f%.$ext}.mkv";
            done
    fi

I wanted to check to see if the extension was actually in the directory after running the first part of the double loop to convert the files. The line

count_file=`ls -1 *.${ext} 2>/dev/null | wc -l

checked to see if there was any files of the extension greater than zero within the directory and, if the count_file quantity was greater than zero, it would run the second section of the double loop, if the quantity was zero it would skip the second part altogether. My process was a little different but it worked, so I'm happy.

  • Related