这是一个脚本..只是想练习一些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有个人资料..我不想要他们的情况)
答案 0 :(得分:1)
你有(我相信第37行):
OUT="$2"
但我认为你的意思是:
OUTS="$2"
答案 1 :(得分:1)
我不完全确定,但最好引用带双引号("
)的文件名而不是printf "%q" "$file"
。
所以替换:
file=`printf "%q" "$file"`
${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2}
与
${TOOL} ${OPT1} "${file}" -o "${OUTS}${file}" ${OPT2}
答案 2 :(得分:1)
首先,使用$()
而不是后退。
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
答案 3 :(得分:0)
您在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
我建议不要在$IFS
循环中使用for
技巧。相反,您可以使用while read file
迭代输入中的每一行。我还建议不要使用printf "%q"
来转义空格,而只是将参数引用到mencoder
,它会将整个内容作为单个参数传递。请注意,我引用${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
顺便说一句,我建议您使用$()
进行命令替换而不是``
;有many reasons为什么它更可取,例如可读性,更合理的引用和转义规则,以及嵌套多级命令替换的能力。 Jonathan和Wes指出的问题值得注意,尽管它们并不是造成你眼前问题的原因。
答案 4 :(得分:0)
使用... | 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}"