0

I have a bash script that spawns another process which takes a long time to execute and often prints lots of error messages into its stdout without stopping.

I would like to read its stdout interactively and then stop it immediately upon matching an error.

Any ideas how to implement this?

For example in the script below I would like to stop the running ffmpeg upon seeing first "Error" in its stdout

#!/bin/bash

FFMPEG="/usr/bin/ffmpeg"
LIST=`find | grep \..`

for i in $LIST; do
    OUTP="$i.txt"
    OUTP_OK="$i.txt.ok"
    TMP_OUTP="$i.tmp"
    if [ -f "$OUTP" -o -f "$OUTP_OK" ] ; then
    echo Skipping "$i"
    else
    echo Checking "$i"...
    RESULT="bad"
    echo "$FFMPEG" -v 5 -i "$i" -f null - 2> "$TMP_OUTP"
    "$FFMPEG" -v 5 -i "$i" -f null - 2> "$TMP_OUTP" && \
        mv "$TMP_OUTP" "$OUTP" && \
        RESULT=`grep -v "\(frame\)\|\(Press\)" "$OUTP" | grep "\["`
    if [ -z "$RESULT" ] ; then
        mv "$OUTP" "$OUTP_OK"
    fi
    fi
done

ffmpeg output:

....
[eac3 @ 0x144a420] frame sync error
Error while decoding stream #0:0
[eac3 @ 0x144a420] frame CRC mismatch
Input stream #0:0 frame changed from rate:48000 fmt:s16 ch:6 to rate:16000 fmt:s16 ch:2
[eac3 @ 0x144a420] frame sync error
Error while decoding stream #0:0
Input stream #0:0 frame changed from rate:16000 fmt:s16 ch:2 to rate:48000 fmt:s16 ch:6
[eac3 @ 0x144a420] frame sync error
Error while decoding stream #0:0
[eac3 @ 0x144a420] frame sync error
Error while decoding stream #0:0
[eac3 @ 0x144a420] frame sync error
Error while decoding stream #0:0
[eac3 @ 0x144a420] frame CRC mismatch
Input stream #0:0 frame changed from rate:48000 fmt:s16 ch:6 to rate:48000 fmt:s16 ch:2
[eac3 @ 0x144a420] frame sync error
Error while decoding stream #0:0
Input stream #0:0 frame changed from rate:48000 fmt:s16 ch:2 to rate:48000 fmt:s16 ch:6
[eac3 @ 0x144a420] frame sync error
Error while decoding stream #0:0
...

...and on and on and on for 20 minutes...

1 Answers1

1

Patching a few other stack overflow posts together, you would:

  1. Run ffmpeg in the background
  2. Use $! to capture the ffmpeg pid
  3. Use grep -q to block until it finds the regex, optionally with a timeout so it doesn't block forever
  4. Kill the pid when the regex is found.

Something like this:

#Run ffmpeg in the background, saving its process id with `S!`
"$FFMPEG" -v 5 -i "$i" -f null - 2> "$TMP_OUTP" &&
FFMPEG_ID=$!
#use "grep -q" to block waiting for regex in TMP_OUTP
#See http://stackoverflow.com/questions/6454915/linux-block-until-a-string-is-matched-in-a-file-tail-grep-with-blocking
timeout 3600 grep -q 'error' <(tail -f $TMP_OUTP)
#We timed out or found the value, kill the process
kill -9 $FFMPEG_ID
lreeder
  • 12,047
  • 2
  • 56
  • 65
  • any way to use grep without timeout (since the execution time of the ffmpeg is unknown)? Also to capture the match status into the main script? (i.e. error found in output or ffmpeg exited by itself without error found) – user3192647 Jan 14 '14 at 15:27
  • Sure, you can use grep w/out timeout. `timeout` is just a utility for running commands with a timeout - see `man timeout` If you want to capture the match status, use `grep -m 1 error` instead of `grep -q error`, which tells grep to exit after it matches "error" once. Note that without a timeout `tail -f` will tail the file indefinitely even after the ffmpeg exits, unless ffmpeg puts something in the output when it exits that can be matched by grep. – lreeder Jan 14 '14 at 17:26