繁体   English   中英

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

[英]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

感谢您的任何帮助。

更新 2

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM