1

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

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"
  • Thank you so much for the detailed answer Ron! I'm getting a syntax error near unexpected token (' for the new line for DIR in (ls -d */) do. I'm going to tinker for a while with that and hopefully once I get that worked out I can try your other suggestions. Thank you again! – InverseTelecine Aug 9 '15 at 1:29
  • Figured out that first syntax error. It needed to be for DIR in $(ls -d */) do. I was missing the $. – InverseTelecine Aug 9 '15 at 1:35
  • Amazing Ron! You're a lifesaver! I used your suggestions and it seems to be working almost perfectly! STARTNO=$(echo $FIRSTFILE | grep -Eo "[0-9]+") works great for getting the file number regardless of it's length. I'd love to ask you how that works exactly, but from reading the grep man page about the -E being an "extended regular expression" it sounds like something that couldn't be explained to a noob quickly. I'll need to read up on it on my own time. – InverseTelecine Aug 9 '15 at 2:16
  • echo "ffmpeg -start_number $STARTNO -i "$DIR$IMGSEQ%04d.$EXTEN" -vcodec ffv1 $VIDNAME.mkv" Right! Thank you for pointing out the $DIR$IMGSEQ mistake. One of several stupid mistakes I knew I was making. Without the $DIR on the beginning there it would be looking for the image files in the present working directory, which the image file would not be in. – InverseTelecine Aug 9 '15 at 2:19
  • (Note: I did change $1STFILE to $FIRSTFILE because I've heard some scripts have a problem with leading numbers in variables.) – InverseTelecine Aug 9 '15 at 2:28

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.