Search code examples
macosbashshellunixmencoder

Bash Script: Can't execute mencoder command!


here's a script.. just wanna practise some bash skills and make a quick util for my chinese mp4 player =)

#!/bin/bash

#####################################
# RockChip 4gb Player mencoder preset
#####################################

TOOL='mencoder'
OUTS='./out/'
OPT1='-noodml'
OPT2="-of avi -ofps 22 -vf-add scale=320:-2,expand=320:240 -srate 44100 -ovc xvid -xvidencopts bitrate=400:max_bframes=0:quant_type=s16le -oac lavc -lavcopts acodec=mp2:abitrate=128"

bold=`tput bold`
normal=`tput sgr0`

# check does argument exists
if test -z "$1"; then
  echo "There's no file given =)"
fi

# Check is it dir or file

if [ -d $1 ]; then
  echo "Directory is given: $1"

  # Test if output argument is given
  if [ -z $2 ]; then
        echo "No output argument given using default: ${bold}${red}$OUTS${normal}"
        mkdir out
  else
      # test is given path a directory
        if [ -d $2 ]; then
                OUT="$2"
        else
           echo "Output argument is not a directory"
        fi
   fi

OLD_IFS=IFS; IFS=$'\n'

for file in `find . -name "*.*" -type f | sed 's!.*/!!'` ; do
        file=`printf "%q" "$file"`
echo    ${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2}
done

IFS=OLD_IFS

fi

Problem is this line:

echo    ${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2}

When you remove echo , to execute command, command fails, but if you'll copy this echoed script, and execute it manually everything works.

When executing command from shell script output is :

MEncoder 1.0rc4-4.2.1 (C) 2000-2010 MPlayer Team
158 audio & 340 video codecs
-of avi -ofps 22 -vf-add scale=320:-2,expand=320:240 -srate 44100 -ovc xvid -xvidencopts bitrate=400:max_bframes=0:quant_type=s16le -oac lavc -lavcopts acodec=mp2:abitrate=128 is not an MEncoder option

Exiting... (error parsing command line)

as I mentioned before executing command manualy everything works for example:

mencoder -noodml 12\ I\ Love\ You\ 1\ \ I\ Love\ You\ 2\ \ I\ Love\ You\ 3.avi -o ./out/12\ I\ Love\ You\ 1\ \ I\ Love\ You\ 2\ \ I\ Love\ You\ 3.avi -of avi -ofps 22 -vf-add scale=320:-2,expand=320:240 -srate 44100 -ovc xvid -xvidencopts bitrate=400:max_bframes=0:quant_type=s16le -oac lavc -lavcopts acodec=mp2:abitrate=128

now all I can do is to copy paste generated commands.. where's the problem? I tried to google really hard.. with no result... (I know that mencoder have profiles.. it's not the case where I want them)


Solution

  • You have this statement before your for loop:

    IFS=$'\n'
    

    This sets your internal field separator to newlines, instead of the default of matching any whitespace. That changes how substituted parameters are parsed. In the line constructing the command:

    ${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2}
    

    what will happen is that each of those ${variable} expressions will be expanded, and then the shell will try splitting them on \n, not on whitespace as you would normally expect. It will give you a similar result as the following would (unless one of these variables contained a newline):

    "${TOOL}" "${OPT1}" "${file}" -o "${OUTS}${file}" "${OPT2}"
    

    Here you can see that you're passing your entire ${OPT2} string in as a single parameter, rather than allowing Bash to split it on spaces and pass each flag in individually. mencoder then gets confused by this one huge parameter that it doesn't know how to deal with. Of course, since the spaces are all still there, they will be printed out by the echo command, and will work fine in a shell in which $IFS has not been reset.

    You can demonstrate this effect pretty easily, by defining a simple function that will print each of its arguments on a separate line:

    $ print_args() { for arg; do echo $arg; done }
    $ foo="1 2"
    $ print_args ${foo} ${foo}
    1
    2
    1
    2
    $ IFS=$'\n'
    $ print_args ${foo} ${foo}
    1 2
    1 2
    

    I would recommend not using the $IFS trick for your for loop. Instead, you can use while read file to iterate over each line in the input. I'd also recommend not using printf "%q" for escaping spaces, but instead just quote the argument to mencoder, which will pass the whole thing in as a single argument. Note that I'm quoting ${file} and ${OUTS}${file} to make sure that they are passed in each as a single argument, but not quoting ${OPT1} and ${OPT2} in order to allow them to be parsed as separate arguments by the shell.

    find . -name "*.*" -type f | sed 's!.*/!!' | while read -r file
    do 
        "${TOOL}" ${OPT1} "${file}" -o "${OUTS}${file}" ${OPT2}
    done
    

    By the way, I'd recommend that you use $() for command substitution rather than ``; there are many reasons why it's preferable, such as readability, more sane quoting and escaping rules within it, and the ability to nest multiple levels of command substitution. And the problems that Jonathan and Wes point out are good to note, though they aren't what are causing your immediate problem.