【问题标题】:Bash script does not continue to read the next line of fileBash 脚本不会继续读取下一行文件
【发布时间】:2010-10-08 02:16:30
【问题描述】:

我有一个 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 的较短脚本。

【问题讨论】:

  • 不将 IFS 设置为 "\n\b" 意味着它正在占用空间吗?
  • 我刚试过"\n",它仍然只读取第一行。我不太擅长 shell 脚本。
  • 并在 processLine 中添加一些回声,以查看传入的内容以及完成处理的时间。也许是 ffmpeg 没有返回。
  • 我认为你是对的。我将通过手动执行一些命令来检查 ffmpeg 正在做什么。
  • 现在您知道为什么要立即将内容置于 CM 控制之下了;所以你可以回到工作版本!

标签: linux bash shell command-line scripting


【解决方案1】:

ffmpeg 读取 STDIN 并耗尽它。解决方案是调用ffmpeg:

 ffmpeg </dev/null ...

详细解释请看这里:http://mywiki.wooledge.org/BashFAQ/089

更新:

从ffmpeg 1.0版本开始,还有-nostdin选项,所以可以用这个代替:

 ffmpeg -nostdin ...

【讨论】:

  • 或使用-nostdin选项调用ffmpeg
  • @Magne :确实,现在这是一个更好的选择。感谢您恢复旧答案,以便我可以更新它。
【解决方案2】:

我也遇到了同样的问题。

我相信 ffmpeg 应对这种行为负责。

我对这个问题的解决方案:

1) 在您的 ffmpeg 命令行末尾使用“&”调用 ffmpeg

2) 由于现在脚本不会等到 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】:

    除非您打算在循环之后从标准输入中读取某些内容,否则您不需要保留和恢复原始标准输入(尽管很高兴看到您知道如何操作)。

    同样,我完全看不出与 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
    

    【讨论】:

      【解决方案4】:

      我会在 eval 之前和之后添加回显,以查看将要评估的内容(以防它将整个文件视为一个大长行)和之后(以防 ffmpeg 命令之一永远占用)。

      【讨论】:

      • 是的,它读取了一些命令错误。例如,某些命令已损坏“os/mp4/4457kb.mp4 /home/test/videos/done/4457kb.flv” - 我已检查文件中是否存在换行符!
      【解决方案5】:

      我认为应该这样做并且似乎是正确的:

      #!/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!
      • 我用各种命令对其进行了测试,它可以工作。也许 ffmpeg 只能运行很长时间?
      • 它是否在最后一个 ffmpeg 之后回显“它需要...”?也许它没有像你想象的那么快完成?
      • 根据转换的不同,ffmpeg 可能会很长。您可以通过不过滤掉标准错误来获得进度状态。
      • 是的,FFmpeg 在某些情况下确实需要一些时间,但 shell 脚本不应该等到命令完成吗??
      猜你喜欢
      • 1970-01-01
      • 2021-04-02
      • 2022-05-30
      • 2014-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      相关资源
      最近更新 更多