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)
You have (line 37 i believe):
OUT="$2"
but I think you meant:
OUTS="$2"
I'm not fully sure but maybe it's better to quote the file name with double quotes ("
) instead of doing printf "%q" "$file"
.
So replace:
file=`printf "%q" "$file"`
${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2}
with
${TOOL} ${OPT1} "${file}" -o "${OUTS}${file}" ${OPT2}
First, use $()
instead of back ticks.
bold=$(tput bold)
normal=$(tput sgr0)
OLD_IFS=IFS; IFS=$'\n'
should be OLD_IFS=$IFS
. you want to get the value of IFS, so put a dollar sign.
You don't need to call sed
to get the base name of files
while read -r file
do
filename="${file##*/}"
filename=$(printf "%q" $filename)
echo mencoder "${OPT1}" "${file}" -o "${OUTS}${file}" "${OPT2}"
done < <(find . -name "*.*" -type f)
lastly,
IFS=OLD_IFS
should be IFS=$OLD_IFS
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.
Using ... | xargs bash -c '...'
you just need to escape embedded single quotes in the file name.
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'
# test cases
file='12 I Love You 1 I Love You 2 I Love You 3.avi'
file='12 I Lo"ve You 1 I Love You 2 I Love You 3.avi' # one embedded double quote
file='12 I Lo"ve You 1 I Lo"ve You 2 I Love You 3.avi' # two embedded double quotes
file="12 I Lo've You 1 I Love You 2 I Love You 3.avi" # one embedded single quote
file="12 I Lo've You 1 I Lo've You 2 I Love You 3.avi" # two embedded single quotes
# escape embedded single quotes in $file
escsquote="'\''"
file="${file//\'/${escsquote}}"
# we're passing ${TOOL} as arg0 to bash -c (which then is available as "$0")
# put the variables in the printf line into single quotes to preserve spaces
# remove no-op ':' to execute the command
printf '%s\n' "${file}"
printf '%s' "${OPT1} '${file}' -o '${OUTS}${file}' ${OPT2}" |
xargs bash -c 'set -xv; printf "%s\n" "$0" "$@" | nl' "${TOOL}"
printf '%s' "${OPT1} '${file}' -o '${OUTS}${file}' ${OPT2}" |
xargs bash -c 'set -xv; : "$0" "$@"' "${TOOL}"
精彩评论