LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - General (https://www.linuxquestions.org/questions/linux-general-1/)
-   -   bash: How can executing an external command confuse a while (read lines from file) loop? (https://www.linuxquestions.org/questions/linux-general-1/bash-how-can-executing-an-external-command-confuse-a-while-read-lines-from-file-loop-4175630055/)

markus-n 05-19-2018 04:24 PM

bash: How can executing an external command confuse a while (read lines from file) loop?
 
Hi,

I have a set of corrupted .mp4 videos and I wrote a bash script to fix them using ffmpeg. Fortunately, only the container is corrupted, and the streams are OK. While fixing, I would like to add titles that are read from a .m3u playlist.

The script reads the .m3u file line by line, ignores empty lines and the leading #EXTM3U, and then it depends on the line content:
If the line contains "EXTINF:", the title is extracted from that line, remembered, and used for the next line that contains a file name. With that information, ffmpeg is executed. No one alters the input .m3u file.

The problem is:
After the first time ffmpeg is really executed, the lines read from the .m3u are incomplete, trimmed or whatever. If I comment out the line executing ffmpeg, the file is parsed as expected.


This is a shortened version of the .m3u playlist:
Code:

#EXTM3U

#EXTINF:2886,01 Premiere
01_Premiere.mp4

#EXTINF:2913,02 Das Leben soll nicht enden
02_Das_Leben_soll_nicht_enden.mp4

#EXTINF:2813,03 Gefährlicher Besucher an Bord
03_Gefaehrlicher_Besucher_an_Bord.mp4

I have verified that it only has unix/Linux file endings (LF).

And this is the script (whith ffmpeg commented out):
Code:

#!/bin/bash

delimiter() {
  echo "================================================================================"
}

INPUTFILE=$1
TARGETDIR=$2

if [ "$INPUTFILE" == "" -o "$TARGETDIR" == "" ]
then
  echo "Input-File and/or target-dir missing."
  exit 1
fi

CURRENT_TITLE=

while read INPUT_LINE
do
  if [ "$INPUT_LINE" == "" -o "$INPUT_LINE" == "#EXTM3U" ]
  then
    continue
  fi

  delimiter
  echo "LINE=\"$INPUT_LINE\""

  if [[ $INPUT_LINE =~ EXTINF: ]]
  then
    CURRENT_TITLE="`echo "$INPUT_LINE" | sed -e 's/.*#EXTINF:[0-9]*,//;'`"
    echo "TITLE=$CURRENT_TITLE"
  else
    if [ -f "$INPUT_LINE" ]
    then
      if [ "$CURRENT_TITLE" != "" ]
      then
        OUTPUTFILE="${TARGETDIR}/${INPUT_LINE##*/}"
        echo "$INPUT_LINE -> $CURRENT_TITLE"
#        ffmpeg -i "$INPUT_LINE" -loglevel quiet -c copy -metadata title="$CURRENT_TITLE" "$OUTPUTFILE"
      else
        echo "ERROR: no current title for file \"$INPUT_LINE\"."
        exit 1
      fi
    else
      echo "ERROR: line \"$INPUT_LINE\" is neither title nor file."
      exit 1
    fi
    CURRENT_TITLE=
  fi
done < <(cat "$INPUTFILE")

As you see, it has some diagnostic echo's.

This is the expected output that I only can achieve with ffmpeg commented out:
Code:

================================================================================
LINE="#EXTINF:2886,01 Premiere"
TITLE=01 Premiere
================================================================================
LINE="01_Premiere.mp4"
01_Premiere.mp4 -> 01 Premiere
================================================================================
LINE="#EXTINF:2913,02 Das Leben soll nicht enden"
TITLE=02 Das Leben soll nicht enden
================================================================================
LINE="02_Das_Leben_soll_nicht_enden.mp4"
02_Das_Leben_soll_nicht_enden.mp4 -> 02 Das Leben soll nicht enden
================================================================================
LINE="#EXTINF:2813,03 Gefährlicher Besucher an Bord"
TITLE=03 Gefährlicher Besucher an Bord
================================================================================
LINE="03_Gefaehrlicher_Besucher_an_Bord.mp4"
03_Gefaehrlicher_Besucher_an_Bord.mp4 -> 03 Gefährlicher Besucher an Bord

But with the ffmpeg line being active, it looks like this:
Code:

================================================================================
LINE="#EXTINF:2886,01 Premiere"
TITLE=01 Premiere
================================================================================
LINE="01_Premiere.mp4"
01_Premiere.mp4 -> 01 Premiere
================================================================================
LINE="oll nicht enden"
ERROR: line "oll nicht enden" is neither title nor file.

So, the line
Code:

#EXTINF:2913,02 Das Leben soll nicht enden
is trimmed to
Code:

oll nicht enden
and therefore not recognized as #EXTINF.

What did I miss ?

Regards,
Markus

RandomTroll 05-19-2018 06:11 PM

Instead of commenting-out the ffmpeg line, echo it, capture the output.

rknichols 05-19-2018 06:34 PM

If the invoked command reads from stdin, that is going to absorb some of the input. I'm not sure why ffmpeg would be doing that, but just redirect its stdin from /dev/null:
Code:

ffmpeg ... <dev/null

markus-n 05-20-2018 12:24 AM

Whow, that's it !
Looks like ffmpeg indeed reads from stdin. Redirecting as you suggested is the solution.

Thank you very much.

MadeInGermany 05-21-2018 04:03 AM

Two comments.
Code:

done < <(cat "$INPUTFILE")
is complicated+slow for
Code:

done < "$INPUTFILE"
In some cases it is advisable to use another file descriptor for the read command (would help here as well, but the </dev/null is the better work around)
Code:

while read INPUT_LINE <&3
do
 ...
done 3< "$INPUTFILE"


Chevron7 05-21-2018 06:52 AM

Also, ffmpeg has the option -nostdin to prevent it reading from stdin.


All times are GMT -5. The time now is 05:21 PM.