[英]Bash script does not continue to read the next line of file
我有一个 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”。 我还利用了 Johannes 的较短脚本。
我认为应该做同样的事情并且似乎是正确的:
#!/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
不?
ffmpeg 读取 STDIN 并耗尽它。 解决方案是调用 ffmpeg :
ffmpeg </dev/null ...
请参阅此处的详细说明:http: //mywiki.wooledge.org/BashFAQ/089
更新:
自 ffmpeg 1.0 版以来,还有-nostdin
选项,因此可以改用:
ffmpeg -nostdin ...
我只是有同样的问题。
我相信 ffmpeg 对这种行为负责。
我对这个问题的解决方案:
1) 在 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
我会在 eval 之前和之后添加 echo 以查看它将要评估的内容(以防它将整个文件视为一大行)和之后(以防 ffmpeg 命令之一永远执行)。
除非您计划在循环后从标准输入中读取某些内容,否则您不需要保留和恢复原始标准输入(尽管很高兴看到您知道如何做)。
同样,我完全看不到使用 IFS 的理由。 退出前肯定没有必要恢复 IFS 的值——这是你正在使用的真正的 shell,而不是 DOS BAT 文件。
当你这样做时:
read var1 var2 var3
shell 将第一个字段分配给$var1
,第二个字段分配给$var2
,并将该行的其余部分分配给$var3
。 在只有一个变量的情况下——例如你的脚本——整行都进入变量,就像你想要的那样。
在流程线函数内部,您可能不想丢弃已执行命令的错误输出。 您可能确实想考虑检查命令的退出状态。 带有错误重定向的echo
是......不寻常的,而且矫枉过正。 如果您非常确定命令不会失败,那么继续忽略错误。 命令是“健谈”吗? 如果是这样,请务必停止聊天。 如果不是,也许您也不需要丢弃标准输出。
当给定多个文件进行处理时,整个脚本可能应该进行诊断,因为它会忽略无关的文件。
您可以仅使用以下方法来简化文件处理:
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.