cebola
cebola

Reputation: 251

Using mkdir and ffmpeg in a batch script on recursive folders

I have many folders with .mp4 files. I wrote a script that takes the mp4 files and converts them into 1 jpeg image per frame. Critically, the output of this script (retained file name, with _frame number appended to the end) goes into a newly created folder with the file basename:

#!/bin/bash
for f in *.mp4; do mkdir -p "${f%.*}" && ffmpeg -i ${f} -start_number 000 "${f%.*}/${f}_%03d.jpg"; done

I can run this for every parent folder one at a time no problem but I would like to just run it once and get it to run recursively.

I changed the code to:

#!/bin/bash
for f in *.mp4/; do mkdir -p "${f%.*}" && ffmpeg -i ${f} -start_number 000 "${f%.*}/${f}_%03d.jpg"; done

and tried playing with the mkdir part (by adding ./) but no matter what I do, I get the following error.

mkdir: cannot create directory ‘*’: Invalid argument

Is there anything I need to do to allow mkdir to create the appropriate new folders in their respective subfolders?

The file structure is as follows:

parent folder
    └── videos_1
        ├── videos_1_basename_1.mp4
        └── videos_2_basename_2.mp4
    └── videos_2
        ├── videos_2_basename_1.mp4
        └── videos_2_basename_2.mp4
.sh script

Upvotes: 1

Views: 1063

Answers (3)

Renaud Pacalet
Renaud Pacalet

Reputation: 28945

You can use the globstar bash option to recurse in sub-directories and also the nullglob option such that the pattern expands as null instead of itself (the reason for your error as explained in another answer):

#!/usr/bin/env bash

shopt -s globstar nullglob
for f in **/*.mp4; do
  d=${f%.mp4}
  b=$(basename -s.mp4 "$f")
  if mkdir -p "$d"; then
    ffmpeg -i "$f" -start_number 000 "$d/${b}_%03d.jpg"
  else
    printf 'could not mkdir %s\n' "$d"
  fi
done

Note: for a video named:

videos_1/videos_1_basename_1.mp4

this will create JPEG files named:

videos_1/videos_1_basename_1/videos_1_basename_1_000.jpg
videos_1/videos_1_basename_1/videos_1_basename_1_001.jpg
...

If you really want to keep the .mp4 extension in the names:

videos_1/videos_1_basename_1/videos_1_basename_1.mp4_000.jpg
...

just remove the -s.mp4 option of basename.

Upvotes: 5

Dudi Boy
Dudi Boy

Reputation: 4865

Suggesting to use find command to get all *.mp4 under current directory.

#!/bin/bash
for currFile in $(find -name "*.mp4"); do
  currDir="${currFile %.mp4}"
  mkdir -p "$currDir"
  ffmpeg -i "$currFile " -start_number 000 "$currDir/${currFile}_%03d.jpg"
done

Upvotes: 1

user1934428
user1934428

Reputation: 22225

*.mp4/ would, due to the appended /, expand to all directories where the name ends in .mp4. If you don't have directories with such a name, you don't get any wildcard expansion. f is therefore set to the string *.mp4/ and ${f%.*} yields a lone *, as you can try by yourself on the command line. It seems that mkdir refuses the creation of directory with such a weird name; I would consider this a feature instead of a bug.

There is no recursion in your code with respect to the directories. You could do a

for f in videos_*/*.m4a

inside your parent folder. Of course f will then also contain the name of the respective subdirectory (for instance videos_1/videos_1_basename_1.m4a), and you have to adapt your script to this fact.

Upvotes: 0

Related Questions