2

I have several video files with different resolutions. Now I want to list only the video filenames with e.g. 720p resolution. I prefer one liners on the bash command line and I can receive helpful video informations including resolution when I execute:

avconv -i video.mp4

but this combination:

find -exec sh -c 'if [[ "$(avconv -i {}") == *720* ]] ; then echo 720 found; fi;'

produces the error: exec - missing argument.

I'm honest, I have hardly fights with bash command structures, especially combined commands. So, what is wrong in my combined command string?

Thank you.

4 Answers4

2

Maybe this command accommplishes what you are looking for:

for i in *.mp4; do if [[ $(avconv -i $i) =~ .*720.* ]]; then echo $i; fi; done
2

It may be surprising but -exec can be used as a test in find invocation:

find -type f -exec sh -c 'ffprobe -show_streams 2>/dev/null "$1" | grep -q coded_height=720' sh {} \; -print

The above command will descend to subdirectories. To search in the current directory only use -maxdepth 1, i.e.:

find -maxdepth 1 -type f -exec …

Note that ffprobe will test all files. It will obviously fail for non-media files but you may get non-video media (like .jpg) in the final output. To avoid this, some additional tests in find should be used before -exec, e.g.:

find -maxdepth 1 -type f \( -iname "*.avi" -o -iname "*.mp4" -o -iname "*.mkv" \) -exec sh -c 'ffprobe -show_streams 2>/dev/null "$1" | grep -q coded_height=720' sh {} \; -print

Or better you can test mime type with file:

find -maxdepth 1 -exec sh -c 'file --mime-type "$1" | grep -q "video/"' sh {} \; -exec sh -c 'ffprobe -show_streams 2>/dev/null "$1" | grep -q coded_height=720' sh {} \; -print

Read man find to learn more.


EDIT:

This command (mis)uses avconv, as you requested:

find -exec sh -c 'file --mime-type "$1" | grep -q "video/"' sh {} \; -exec sh -c 'avconv 2>&1 -i "$1" | grep -q "Stream.*x720"' sh {} \; -print

The issue is every invocation of avconv therein throws an error. We just ignore it and extract the information we need. This is somewhat ugly solution. I'm not entirely sure your avconv behaves as mine, you may need to replace Stream.*x720 with some other regex.


what is wrong in my combined command string?

  • find -exec requires closing with \; or +,
  • there is $( without ) after you edited the question there are "" interleaved (not nested) with $(),
  • *720* may trigger shell globbing, should be "*720*" (I'm talking about sh, not the outer shell),
  • the output of avconv you try to parse goes to stderr, I think you need to redirect it before you can parse it,
  • and maybe something else.
1

I don't know avconv, I usually use ffmpeg. If you install it you could use this script

#!/bin/bash
OIFS="$IFS"
IFS=$'\n'

files=$(find ./ -type f -exec ls {} + | grep "..mpg$|..avi$|..mkv$|..mp4$"| cut -c3-)

for f in ${files} do resolution=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=s=x:p=0 ${f}) printf "%-100s %-10s\n" $f $resolution done

0

For the moment I have found a usable solution:

 for i in ls *.m??; do sm=$(mediainfo $i | grep Height | sed 's/  //g');if [[ "$sm" == *720* ]]; then printf "Video: %-s $i %-s $sm \n"; fi; done

Here I can loop all m?? video files and print its name including "720" condition. Without the if part I could let show me all videos and their resolutions.

I worked this out with the help of other answers here.

When I try to combine find and exec and the do part of the solution above, I only receive "E: File read error" messages as result from mediainfo for each subject:

 find -exec mediainfo "{}" \; -exec bash -c 'sm=$(mediainfo ${} | grep 720) : ; echo $sm' \;

As I said, the 'if' construction works (for the actual directory), but I will try to make it run with find also.