简体   繁体   English

Bash脚本:无法执行mencoder命令!

[英]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 =) 这是一个脚本..只是想练习一些bash技巧,并为我的中国mp4播放器做一个快速实用程序=)

#!/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. 当您删除echo,执行命令时,命令失败,但是如果您复制此回显脚本并手动执行,则一切正常。

When executing command from shell script output is : 从shell脚本执行命令时,输出为:

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) 我试图用谷歌真的很难..没有结果...(我知道mencoder有个人资料..不是我想要他们的情况)

You have (line 37 i believe): 您有(我相信第37行):

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" . 我不确定,但是最好用双引号( " )引用文件名,而不是用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 . OLD_IFS=IFS; IFS=$'\\n'应该是OLD_IFS=$IFS you want to get the value of IFS, so put a dollar sign. 您想获得IFS的价值,因此请加一个美元符号。

You don't need to call sed to get the base name of files 您无需调用sed即可获取文件的基本名称

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 IFS=OLD_IFS应该是IFS=$OLD_IFS

You have this statement before your for loop: for循环之前,您具有以下语句:

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. 将会发生的是,每个${variable}表达式都将被扩展,然后外壳程序将尝试在\\n而不是您通常期望的空白处拆分它们。 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. 在这里,您可以看到您将整个${OPT2}字符串作为单个参数${OPT2} ,而不是让Bash在空格上将其分割并分别传递每个标志。 mencoder then gets confused by this one huge parameter that it doesn't know how to deal with. 然后, mencoder对这个不知道如何处理的巨大参数感到困惑。 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. 当然,由于所有空格仍然存在,它们将通过echo命令打印出来,并且在$IFS尚未重置的shell中可以正常工作。

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. 我建议不要在for循环中使用$IFS技巧。 Instead, you can use while read file to iterate over each line in the input. 相反,您可以使用while read file来迭代输入中的每一行。 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. 我也建议不要使用printf "%q"来转义空格,而只需将参数引用给mencoder ,它将把整个事情作为单个参数传递。 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. 请注意,我引用${file}${OUTS}${file}以确保它们作为单个参数传入,但不引用${OPT1}${OPT2}以便允许它们被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 '...' ... | xargs bash -c '...' you just need to escape embedded single quotes in the file name. ... | xargs bash -c '...'您只需要对文件名中的嵌入式单引号进行转义。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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