【问题标题】:Bash Script: Can't execute mencoder command!Bash 脚本:无法执行 mencoder 命令!
【发布时间】:2011-04-21 04:25:28
【问题描述】:

这是一个脚本.. 只是想练习一些 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

问题是这一行:

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

当您删除 echo 以执行命令时,命令会失败,但如果您复制此回显脚本并手动执行它,一切正常。

当从 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)

正如我在手动执行命令之前提到的,一切正常,例如:

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

现在我能做的就是复制粘贴生成的命令。问题出在哪里?我真的很努力地用谷歌搜索......没有结果......(我知道 mencoder 有个人资料......我不想要他们)

【问题讨论】:

  • 对 OLD_IFS 和 IFS 的赋值在 RHS 上缺少美元符号,并且 RHS 也应该用双引号括起来:OLD_IFS="$IFS"; IFS="$OLD_IFS"。但是,由于脚本即将退出,因此重置该值并不重要(因此保存它也不重要)。

标签: macos bash shell unix mencoder


【解决方案1】:

你有(我相信第 37 行):

OUT="$2"

但我认为你的意思是:

OUTS="$2"

【讨论】:

    【解决方案2】:

    我不完全确定,但也许最好用双引号 (") 引用文件名而不是 printf "%q" "$file"

    所以替换:

    file=`printf "%q" "$file"`
    ${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2}
    

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

    【讨论】:

      【解决方案3】:

      首先,使用$() 而不是反引号。

      bold=$(tput bold)
      normal=$(tput sgr0)
      

      OLD_IFS=IFS; IFS=$'\n' 应该是 OLD_IFS=$IFS 。你想得到 IFS 的值,所以放一个美元符号。

      您无需调用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)
      

      最后,

      IFS=OLD_IFS 应该是IFS=$OLD_IFS

      【讨论】:

        【解决方案4】:

        您在 for 循环之前有此语句:

        IFS=$'\n'
        

        这会将您的内部字段分隔符设置为换行符,而不是默认匹配任何空格。这改变了替换参数的解析方式。在构造命令的行中:

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

        将会发生的情况是每个${variable} 表达式都将被扩展,然后shell 将尝试将它们拆分为\n,而不是您通常期望的空白。它将为您提供与以下类似的结果(除非这些变量之一包含换行符):

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

        在这里您可以看到您将整个 ${OPT2} 字符串作为单个参数传递,而不是允许 Bash 将其拆分为空格并单独传递每个标志。 mencoder 然后被这个不知道如何处理的巨大参数弄糊涂了。当然,由于空格仍然存在,它们将被echo 命令打印出来,并且在$IFS 没有被重置的shell 中可以正常工作。

        你可以很容易地演示这个效果,通过定义一个简单的函数,将它的每个参数打印在单独的行上:

        $ 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
        

        我建议不要在 for 循环中使用 $IFS 技巧。相反,您可以使用while read file 遍历输入中的每一行。我还建议不要使用printf "%q" 来转义空格,而只需将参数引用到mencoder,这会将整个内容作为单个参数传递。请注意,我引用 ${file}${OUTS}${file} 以确保它们作为单个参数传入,但没有引用 ${OPT1}${OPT2} 以允许它们被解析为单独的参数壳。

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

        顺便说一句,我建议您使用$() 进行命令替换,而不是``;有many reasons 为什么它更可取,例如可读性、更合理的引用和转义规则,以及嵌套多级命令替换的能力。 Jonathan 和 Wes 指出的问题值得注意,尽管它们并不是导致您眼前问题的原因。

        【讨论】:

        • 不错的朋友 =)!当我将所有带空格的文件名都用 \n.. 分割时,IFS 真是令人头疼
        【解决方案5】:

        使用... | 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}"
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-01-04
          • 1970-01-01
          • 2016-07-25
          • 2012-10-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多