Bash脚本不会继续读取下一行文件

时间:2009-02-17 13:39:53

标签: linux bash shell command-line scripting

我有一个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

感谢您的帮助。

更新2

它的ffmpeg命令而不是无效的shell脚本。但我应该像保罗指出的那样只使用“\ b”。我也在利用约翰内斯的短篇小说。

5 个答案:

答案 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命令自动报告错误(并在它们之后继续)并处理所有输入文件,或者如果没有参数则读取标准输入。在变量周围使用双引号意味着它作为一个单元传递(因此未解析为单独的单词)。

使用datebc很有意思 - 之前我没见过。

总而言之,我会看到类似的东西:

#!/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