Search code examples
bashshellffmpegfind

How to use find to match substring of files to be concatted?


complete beginner here, so sorry if this is painfully obvious. I'm trying to write a shell script to ffmpeg concat protocol a bunch of split video files together by looping over the files and dynamically adding the correct parts to be joined together. For example, turning this:

titlea-00001234-01.ts
titlea-00001234-02.ts
titlea-00001234-03.ts
titleb-00001234-01.ts
titleb-00004321-02.ts
titleb-00004321-03.ts

into this:

titlea-00001234.mp4
titleb-00004321.mp4

by doing this

ffmpeg -i "concat:titlea-00001234-01.ts|titlea-00001234-02.ts|titlea-00001234-03.ts" -c copy titlea-00001234.mp4
ffmpeg -i "concat:titleb-00001234-01.ts|titleb-00001234-03.ts|titleb-00001234-03.ts" -c copy titleb-00001234.mp4

But what I'm having trouble with is using find to add the correct parts after "concat:".

Here's my best attempt:

#!/bin/bash
for i in /path/to/files/*.ts
    do
        if [[ "$i" =~ (-01) ]]
        then
            j="${i##*/}"
            k="${j%-0*}"
            ffmpeg -i `concat:( find /path/to/files/ -type f -name "{$k}*" -exec printf "%s\0" {} + )` -c copy /path/to/output/"{$k%.ts}.mp4"
        else
            echo "no files to process"
            exit 0
        fi
    done

But this gives an error of "No such file or directory".
Edit This solution worked perfectly for my needs https://stackoverflow.com/a/75807616/21403800 Thanks @pjh and everyone else for taking the time to help


Solution

  • Try this Shellcheck-clean code:

    #! /bin/bash -p
    
    shopt -s nullglob
    
    cd path_to_files || exit 1
    
    prev_base=
    for tsfile in *-[[:digit:]][[:digit:]].ts ''; do
        base=${tsfile%-*}
        if [[ $base == "$prev_base" ]]; then
            iarg+="|$tsfile"
        else
            [[ -n $prev_base ]] && echo ffmpeg -i "$iarg" -c copy "${prev_base}.mp4"
            iarg="concat:$tsfile"
            prev_base=$base
        fi
    done
    
    • shopt -s nullglob makes globs expand to nothing when nothing matches (otherwise they expand to the glob pattern itself, which is almost never useful in programs).
    • The '' before the semicolon in for tsfile in *-[[:digit:]][[:digit:]].ts ''; do is an empty string sentinel to cause the last batch of files matched by the *-[[:digit:]][[:digit:]].ts pattern to be processed by the code in the loop body.
    • See Removing part of a string (BashFAQ/100 (How do I do string manipulation in bash?)) for an explanation of ${tsfile%-*}.
    • The code currently just prints the ffmpeg commands to be executed. Remove the echo to have it actually run the ffmpeg commands.