我有一个shell脚本,用于保存执行到CSV文件的命令的输出。它从shell脚本中读取它必须执行的命令:
ffmpeg -i /home/test/videos/avi/418kb.avi /home/test/videos/done/418kb.flv
ffmpeg -i /home/test/videos/avi/1253kb.avi /home/test/videos/done/1253kb.flv
ffmpeg -i /home/test/videos/avi/2093kb.avi /home/test/videos/done/2093kb.flv
您可以看到每一行都是一个ffmpeg命令。但是,脚本只执行第一行。就在一分钟前,它几乎完成了所有命令。由于某种原因它失踪了一半。我编辑了包含命令的文本文件,现在它只执行第一行。这是我的bash脚本:
#!/bin/bash
# Shell script utility to read a file line line.
# Once line is read it will run processLine() function
#Function processLine
processLine(){
line="$@"
START=$(date +%s.%N)
eval $line > /dev/null 2>&1
END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)
echo "$line, $START, $END, $DIFF" >> file.csv 2>&1
echo "It took $DIFF seconds"
echo $line
}
# Store file name
FILE=""
# get file name as command line argument
# Else read it from standard input device
if [ "$1" == "" ]; then
FILE="/dev/stdin"
else
FILE="$1"
# make sure file exist and readable
if [ ! -f $FILE ]; then
echo "$FILE : does not exists"
exit 1
elif [ ! -r $FILE ]; then
echo "$FILE: can not read"
exit 2
fi
fi
# read $FILE using the file descriptors
# Set loop separator to end of line
BAKIFS=$IFS
IFS=$(echo -en "\n\b")
exec 3<&0
exec 0<$FILE
while read line
do
# use $line variable to process line in processLine() function
processLine $line
done
exec 0<&3
# restore $IFS which was used to determine what the field separators are
BAKIFS=$ORIGIFS
exit 0
感谢您的帮助。
它的ffmpeg命令而不是无效的shell脚本。但我应该像保罗指出的那样只使用“\ b”。我也在利用约翰内斯的短篇小说。
答案 0 :(得分:4)
我认为应该这样做并且似乎是正确的:
#!/bin/bash
CSVFILE=/tmp/file.csv
cat "$@" | while read line; do
echo "Executing '$line'"
START=$(date +%s)
eval $line &> /dev/null
END=$(date +%s)
let DIFF=$END-$START
echo "$line, $START, $END, $DIFF" >> "$CSVFILE"
echo "It took ${DIFF}s"
done
没有
答案 1 :(得分:3)
ffmpeg读取STDIN并耗尽它。解决方案是使用以下命令调用ffmpeg:
ffmpeg </dev/null ...
请参阅此处的详细说明:http://mywiki.wooledge.org/BashFAQ/089
答案 2 :(得分:2)
我遇到了同样的问题。
我相信ffmpeg对此行为负责。
我解决这个问题的方法:
1)用“&amp;”调用ffmpeg在ffmpeg命令行的末尾
2)从现在开始,skript不会等到ffmpeg进程完成, 我们必须阻止我们的脚本启动几个ffmpeg进程。 我们通过至少延迟循环传递来实现这一目标 一个正在运行的ffmpeg进程。
#!/bin/bash
cat FileList.txt |
while read VideoFile; do
<place your ffmpeg command line here> &
FFMPEGStillRunning="true"
while [ "$FFMPEGStillRunning" = "true" ]; do
Process=$(ps -C ffmpeg | grep -o -e "ffmpeg" )
if [ -n "$Process" ]; then
FFMPEGStillRunning="true"
else
FFMPEGStillRunning="false"
fi
sleep 2s
done
done
答案 3 :(得分:1)
我会在eval之前和之后添加echos以查看eval的内容(如果它将整个文件视为一个大的长行)和之后(如果其中一个ffmpeg命令永远占用)。
答案 4 :(得分:1)
除非您计划在循环后从标准输入读取内容,否则您不需要保留和恢复原始标准输入(尽管很高兴看到您知道如何)。
同样,我根本没有看到与IFS搭讪的原因。在退出之前肯定没有必要恢复IFS的值 - 这是你正在使用的真实shell,而不是DOS BAT文件。
当你这样做时:
read var1 var2 var3
shell会将第一个字段分配给$var1
,将第二个字段分配给$var2
,将其余部分分配给$var3
。在只有一个变量的情况下 - 例如你的脚本 - 整行进入变量,就像你想要的那样。
在流程线功能中,您可能不希望从执行的命令中丢弃错误输出。您可能想要考虑检查命令的退出状态。带错误重定向的echo
是......异常,而且过度杀伤。如果您足够确定命令不会失败,那么继续忽略错误。命令'chatty';如果是这样,一定要扔掉聊天。如果没有,也许您不需要丢弃标准输出。
作为一个整体的脚本应该可以诊断何时给它处理多个文件,因为它忽略了无关的文件。
您可以通过以下方式简化文件处理:
cat "$@" |
while read line
do
processline "$line"
done
cat
命令自动报告错误(并在它们之后继续)并处理所有输入文件,或者如果没有参数则读取标准输入。在变量周围使用双引号意味着它作为一个单元传递(因此未解析为单独的单词)。
使用date
和bc
很有意思 - 之前我没见过。
总而言之,我会看到类似的东西:
#!/bin/bash
# Time execution of commands read from a file, line by line.
# Log commands and times to CSV logfile "file.csv"
processLine(){
START=$(date +%s.%N)
eval "$@" > /dev/null
STATUS=$?
END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)
echo "$line, $START, $END, $DIFF, $STATUS" >> file.csv
echo "${DIFF}s: $STATUS: $line"
}
cat "$@" |
while read line
do
processLine "$line"
done