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:
- 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. - 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
andrenderSeven001.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. - 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) - (minor problem: 1. I know using
ls
infor 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]+")
${FIRSTFILE%.*}
takes the first file and strips off the extension.- grep is pattern searching
- The
-i
part of-iEo
is to make grep accept uppercase and lowercase pattern matches. - The
-E
part of-iEo
is to (quoting fromman grep
) "Interpret PATTERN as an extended regular expression." Whatever that means. - The
-o
part of-iEo
is to only print the matching pattern. [a-z]
is to match the pattern of any letter of the alphabet+
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.