2

I want to move all video files with a certain duration to some directory.  In this answer I found a way to probe the duration of a video file:

ffprobe -i some_video -show_entries format=duration -v quiet -of csv="p=0"

I wanted to do something like this with it:

find . -test <test for duration> -exec mv {} /path/to/target ';'

However, it seems that find does not support arbitrary scripts for the test (there is no -test switch). Did I miss something? Is this simply not possible with find? Am I better off just writing a python script?

user1785730
  • 451
  • 1
  • 7
  • 22

1 Answers1

2

You can use -exec as your -test.  Write a script that takes one filename argument and exits 0 if the file meets your condition and non-zero otherwise.  I don’t have the FF software (e.g., ffprobe) software on my system, so I can’t check this, but something like

#!/bin/sh
if dur=$(ffprobe -i "$1" -show_entries format=duration -v quiet -of csv="p=0")
then
        [ "$dur" -ge 900 ]
        exit
else
        exit
fi

would check for a video having duration ≥ 15 minutes, assuming ffprobe outputs an integer number of seconds.  (If it reports mm:ss or ss.ddd, I leave the trivial modifications to you, but see my example below for inspiration.)  Explanation: the if fails if ffprobe fails; in that case, we drop to the lower exit and exit with the error status from ffprobe.  If ffprobe succeeds, we test its output ("$dur") and then exit with the result of that test.  This could also be written

[ "$dur" -ge 900 ]  &&  exit 0  ||  exit 1

In fact, the entire script can really be reduced to

dur=$(ffprobe -i "$1" -show_entries format=duration -v quiet -of csv="p=0")  \
                            &&  [ "$dur" -ge 900 ]

So call your script something like check_dur; then you can do

find . -exec ./check_dur {} ';'-exec mv {} /path/to/target ';'

This works because, as I said, -exec functions as a test, and things after a -exec will be processed only if the first -exec succeeded (exited with status 0).

As I said, I don’t have the FF software (e.g., ffprobe) software on my system, so I tested with this script:

#!/bin/sh
if mod=$(stat -c%y "$1")
then
        mod=${mod#*-}
        mod=${mod%%-*}
        mod=${mod#0}
        [ $(( mod%2 )) = 0 ]
        exit
else
        exit
fi

which tests whether the modification date of a file or directory is in an even-numbered month.  Note: on my (en_US) system, stat -c%y filename outputs the modification date in the format yyyy-mm-dd HH:MM:SS.ddddddddd -(TZ).  So the above code

  1. strips off the year (from the left),
  2. strips off everything after the month, and
  3. strips off a leading 0, if any (because $((…)) interprets a leading zero as an indicator that a number is octal, and so it reports an error for 08 and 09).

A couple of other notes:

  1. For mv, the command can be improved a little by changing it to

     find . -exec ./check_dur {} ';' -exec mv -t /path/to/target {} +
    

which will execute mv once with many files.  You cannot do this with the first -exec, because -exec ... + cannot be used as a test.  2. If you don’t want to write a separate script, you can use sh -c and inline the script; e.g.,

    find . -exec sh -c 'dur=$(ffprobe -i "$1" -show_entries format=duration -v quiet -of csv="p=0") && [ "$dur" -ge 900 ]' sh {} ';'-exec mv -t /path/to/target {} +

This creates a one-line mini-script (the string after the sh -c) and passes the filename ({}) to it as an argument.  Some versions of find will let you incorporate the {} into the mini-script, but this is a bad idea:

  • It doesn’t work for all versions of find, and
  • even when it works, in general, it will fail (and maybe even fail catastrophically) for some filenames.
Benjamin Loison
  • 183
  • 1
  • 6