Bash script to convert multiple image sequences to video files

I'm trying to make a script to take multiple series of image sequences within subdirectories of the present working directory and send them through ffmpeg to create video files in the present working directory named after the directories. It's complicated by the fact that the image sequence names vary, as does the starting number of the sequences, but the real problem is I'm new to bash scripting. Here is an example of the files:

directoryFirstRender01 renderOne0032.png renderOne0033.png renderOne0034.png
directoryFourthRender01 renderFour4253.png renderFour4254.png renderFour4255.png
directoryFifthRender01 renderFiveTwo1367.png renderFiveTwo1368.png renderFiveTwo1369.png

Here is a rough outline of the script that has several problems that I know about and probably many more that I don't. The reason I don't know how many problems it has is because I've gotten stuck getting and parsing the filenames, and without that working I can't test the rest:

#!/bin/bash
for DIR in (ls -d */); do 1STFILE=(ls $dir/ | sort -n | head -1) STARTNO=${1STFILE:(-8):4} IMGSEQ=${1STFILE%????.*} EXTEN=${1STFILE*.} ffmpeg -start_number $STARTNO -i $IMGSEQ%04d.$EXTEN $DIR.mkv
done

My current main problems that I know about are these:

  1. I don't know how to get the filename of just the first file in the directories. ls dir/ | sort -n | head -1 works in the terminal, but not in a script.
  2. The substring extraction for getting the first number of the first file would only work on image sequences of 4 digits. Is there a way to make it accept renderSix00001.png-renderSix99999.png and renderSeven001.png-renderSeven999.png too? That's also a problem with the line getting $IMGSEQ, and the %04d in the ffmpeg line would need to be changed somehow.
  3. I think I'm going to have trouble with the $IMGSEQ variable mixing with the %04d in the ffmpeg input filename without brackets, but I haven't gotten that far yet. What syntax do I use there $IMGSEQ(%04d).$EXTEN or $IMGSEQ {%04d}.$EXTEN maybe? (Stuff like this is why it sucks to be jumping in the deep end of scripting)
  4. (minor problem: 1. I know using ls in for DIR in (ls -d */) is frowned upon, but I don't know how else to filter out any files in the present working directory and just work on the directories.)

I'm sure the script looks like a complete mess. Any overall guidance to get the whole thing working would be very welcome, but if no one has the patience to walk me through the whole thing if I can just get some help getting and parsing the filenames maybe I can fix the rest myself. Thank you.

UPDATE 1: (After applying Ron's suggestions)

Thank you again Ron! This is the near-final script:

#!/bin/bash
for DIR in $(ls -d */)
do FIRSTFILE=$(ls $DIR | sort -n | head -1) STARTNO=$(echo $FIRSTFILE | grep -Eo "[0-9]+") DIGITNO=${#STARTNO} IMGSEQ=${FIRSTFILE%????.*} EXTEN=${FIRSTFILE#*.} OUTPUT=${DIR%/} ffmpeg -start_number $STARTNO -i "$DIR$IMGSEQ"%0"$DIGITNO"d".$EXTEN" $OUTPUT.mkv
done

Since thanks to Ron the $STARTNO variable will now accept multiple digit lengths, I'm using the length parameter expansion in the $DIGITNO line to get the number to use in the %04d part to make it %03d or %05d as needed so that will work too. I'm hoping the liberal use of " to separate the %04d from the surrounding variables isn't considered bad form (if it was, what is the right way?). The ONLY missing part of the script (I think!) is how to make $IMGSEQ accept multiple digit lengths. I have no ideas for that right now. If anyone knows, I would appreciate knowing, but maybe I can find it combing through parameter expansion documentation, as if I haven't had more than enough of that for one lifetime. :)

UPDATE 2: (Success!)

Figured it out! (How to get IMGSEQ to accept names involving multiple digit lengths that is) Working based on Ron's suggestion for $STARTNO using (echo $FIRSTFILE | grep -Eo "[0-9]+") I came up with

IMGSEQ=$(echo ${FIRSTFILE%.*} | grep -iEo "[a-z]+")
  1. ${FIRSTFILE%.*} takes the first file and strips off the extension.
  2. grep is pattern searching
  3. The -i part of -iEo is to make grep accept uppercase and lowercase pattern matches.
  4. The -E part of -iEo is to (quoting from man grep) "Interpret PATTERN as an extended regular expression." Whatever that means.
  5. The -o part of -iEo is to only print the matching pattern.
  6. [a-z] is to match the pattern of any letter of the alphabet
  7. + tells grep that the previous alphabet pattern should be matched "one or more times" (so do it over and over to get all the letters).

A limitation of that is that it wouldn't handle filenames with special characters like - or _, but I can live with that for now.

So here is the semi-final script!

#!/bin/bash
for DIR in $(ls -d */)
do FIRSTFILE=$(ls $DIR | sort -n | head -1) STARTNO=$(echo $FIRSTFILE | grep -Eo "[0-9]+") DIGITNO=${#STARTNO} IMGSEQ=$(echo ${FIRSTFILE%.*} | grep -iEo "[a-z]+") EXTEN=${FIRSTFILE#*.} OUTPUT=${DIR%/} ffmpeg -n -start_number $STARTNO -i "$DIR$IMGSEQ"%0"$DIGITNO"d".$EXTEN" $OUTPUT.mkv
done

So run in a directory with some directories named like my example directories above (except I added one with an image sequence padded with more than 4 zeros to make sure that worked too), this script will run the following ffmpeg lines one after the other:

ffmpeg -n -start_number 0032 -i directoryFirstRender01/renderOne%04d.png directoryFirstRender01.mkv
ffmpeg -n -start_number 4253 -i directoryFourthRender01/renderFour%04d.png directoryFourthRender01.mkv
ffmpeg -n -start_number 01367 -i directoryFifthRender01/renderFiveTwo%05d.png directoryFifthRender01.mkv

Freakin' perfect! Thanks so much again for your help Ron! I definitely would not have been able to finish without your help.

1 Answer

I don't know how to get the filename of just the first file in the directories. ls dir/ | sort -n | head -1 works in the terminal, but not in a script.

You may want to re-write:

for DIR in (ls -d */); do 1STFILE=(ls $dir/ | sort -n | head -1)

as

for DIR in (ls -d */)
do 1STFILE=$(ls $DIR | sort -n | head -1)

Is there a way to make it accept renderSix00001.png-renderSix99999.png and renderSeven001.png-renderSeven999.png too?

If you've followed the above then:

STARTNO=$(echo $1STFILE | grep -Eo "[0-9]+")

should get all the numbers in the filename.

What syntax do I use there $IMGSEQ(%04d).$EXTEN or $IMGSEQ {%04d}.$EXTEN maybe?

Put that thing inside "" so:

ffmpeg -start_number $STARTNO -i "$IMGSEQ%4d.$EXTEN" "$DIR.mkv"

Now, if you use just "$IMGSEQ%4d.$EXTEN" you are going to hit a problem (try it to see what it is!). So make it "$DIR$IMGSEQ%4d.$EXTEN"

If you use "$DIR.mkv" it is going to create a hidden file .mkv which is not what you want. One not-so-cool way I thought is to store only the name of directory by stripping the suffix / in a variable:

OUTPUT=$(echo $DIR | sed 's/\/$//')

and use this in ffmpeg as in:

ffmpeg -start_number $STARTNO -i "$DIR$IMGSEQ%4d.$EXTEN" "$OUTPUT.mkv"
7

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like