简体   繁体   中英

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' 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 '...' ... | 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}"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM