1

Input:

# cat list
video1.mp4
video2.mp4
video3.mp4
video4.mp4

My script:

#!/bin/bash

while IFS= read -r line || [[ -n "$line" ]]; do
  echo $line
  ffmpeg -i $line -c copy -bsf:v h264_mp4toannexb -f mpegts $line.ts
  echo $line

done < "./list";

rm *.ts

Every second iterations, bash reads "ideo2.mp4" instead of "video2.mp4" or "ideo4.mp4" instead of "video4.mp4"

FULL output

+ IFS=
+ read -r line
+ echo video1.mp4
video1.mp4
+ ffmpeg -i video1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts video1.mp4.ts
...
+ echo video1.mp4
video1.mp4
+ IFS=
+ read -r line
+ echo ideo2.mp4
ideo2.mp4
+ ffmpeg -i ideo2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts ideo2.mp4.ts
...
ideo2.mp4: No such file or directory
+ echo ideo2.mp4
ideo2.mp4
+ IFS=
+ read -r line
+ echo video3.mp4
video3.mp4
+ ffmpeg -i video3.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts video3.mp4.ts
...
+ echo video3.mp4
video3.mp4
+ IFS=
+ read -r line
+ echo ideo4.mp4
ideo4.mp4
+ ffmpeg -i ideo4.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts ideo4.mp4.ts
...
ideo4.mp4: No such file or directory
+ echo ideo4.mp4
ideo4.mp4

When I comment line "ffmpg..." everything works fine.
Testing on local Ubuntu (bash 4.4.20(1)-release) and on VPS (debian) (bash 4.4.12(1))
What is going on?

LubieCiastka
  • 143
  • 1
  • 3
  • 9
  • I suspect the file has CRLF line endings, fix it with `dos2unix` – Barmar Mar 19 '20 at 22:45
  • working only on Linux. Files created via nano – LubieCiastka Mar 19 '20 at 22:48
  • Put `set -x` at the beginning of the script, so you see all the statements as they're executing, with variables expanded. – Barmar Mar 19 '20 at 22:52
  • 1
    Try this: `for line in $(< ./list); do [[ -n "$line" ]] || continue; echo $line; ffmpeg -i $line -c copy -bsf:v h264_mp4toannexb -f mpegts $line.ts; echo $line; done` It’s not perfect but it should be able to test the theory of ffmpeg eating stdin. – vdavid Mar 19 '20 at 23:14
  • Your comment also solves the problem – LubieCiastka Mar 19 '20 at 23:23
  • 1
    See [BashFAQ #89: I'm reading a file line by line and running ssh or ffmpeg, only the first line gets processed!](http://mywiki.wooledge.org/BashFAQ/089) – Gordon Davisson Mar 20 '20 at 06:22

1 Answers1

2

When I comment line "ffmpg..." everything works fine.

You're passing the contents of ./list as standard input for the entire loop, meaning that all commands in the loop can read from that standard input.

I've never used ffmpeg, but presumably it's reading one byte from standard input for some reason. If you add < /dev/null to the ffmpeg command (so that it has its own, empty standard input rather than inheriting from the shell), it won't be able to mess with the standard input used by the rest of the loop.


Incidentally, your quoting is wrong. There's no need to quote ./list (since it has no special characters or expansions), and by contrast, you should quote $line wherever it appears (because it's easier to add the quotation marks, and to be in the habit of always adding quotation marks when there are expansions, than to prove to yourself every single time that the quotation marks are never needed in a given case). So:

ffmpeg -i "$line" -c copy -bsf:v h264_mp4toannexb -f mpegts "$line.ts" < /dev/null
ruakh
  • 175,680
  • 26
  • 273
  • 307
  • and thanks for tip with "quoting" – LubieCiastka Mar 19 '20 at 23:20
  • 2
    @LubieCiastka You can use the `-nostdin` global option in ffmpeg instead of `ffmpeg ... < /dev/null` (and this question has been asked on SO a few times so it may get marked as a duplicate). – llogan Mar 20 '20 at 00:41
  • 1
    In this case, maybe `-y` [should be set as well](https://stackoverflow.com/questions/28372647/how-to-force-ffmpeg-into-non-interactive-mode). – user1934428 Mar 20 '20 at 07:55