64

I want to count the total number of files in particular directory that ends with ".mp4" extension.

I am getting following command:

ls -F |grep -v / | wc -l

It count all the files in particular directory, but I want the count of files that end with .mp4 extension.

Is there any Ubuntu command for that?

fossfreedom
  • 171,546
  • 47
  • 376
  • 404
Prakash V Holkar
  • 2,521
  • 7
  • 20
  • 29
  • 2
    The simple, correct, straightforward answer is @louis-matthijssen one. `ls -1` handling of newline characters make, in this case, sensible to parse `ls` output. The marked one is wrong for the use of -R flag. – Rmano Apr 24 '14 at 16:25

10 Answers10

61

Unfortunately this benign problem is difficult to solve in a way which supports all file names and is portable. This is safe (it handles hidden files, paths containing spaces, dashes and even newlines) and POSIX compatible:

find /path/to/directory -mindepth 1 -type f -name "*.mp4" -printf x | wc -c

If you don't want it to be recursive, simply add -maxdepth 1.

You shouldn't parse ls output.

Test:

$ cd -- "$(mktemp -d)"
$ touch -- -foo.mp4 .bar.mp4 .bat.mp4 'baz.mp4
> ban.mp4'
$ find . -mindepth 1 -type f -name "*.mp4" -exec printf x \; | wc -c
4

Compare with the accepted answer:

$ ls -lR ./*.mp4 | wc -l
3

Or other suggestions:

$ find . -name "*.mp4" | wc -l
5
$ ls -1 *.mp4 | wc -l
ls: invalid option -- '.'
Try 'ls --help' for more information.
0
$ find . -name "*.mp4" | wc -c # Answer fixed at a later time
51
$ find . -name "*.mp4" | wc -l
5
$ find . | grep -i ".mp4$" | wc -l
5
$ ls . | grep ".mp4$" | wc -l
3
l0b0
  • 8,529
  • 8
  • 42
  • 67
  • 2
    Although this answer is correct and robust, you can use `-printf x` instead of `-exec printf x \;`. That is: `find /path/to/directory -mindepth 1 -type f -name "*.mp4" -printf x | wc -c` There's no need to `-exec` the external `printf` command, which if there are many files will be *very slow*, because `find` has to [fork(2)](http://manpages.ubuntu.com/manpages/xenial/en/man2/fork.2.html) off a copy of itself and then [execve(2)](http://manpages.ubuntu.com/manpages/xenial/en/man2/execve.2.html) `/usr/bin/printf`. With `-exec printf x \;`, that must be done once for *each file*. – Eliah Kagan Oct 10 '17 at 17:39
  • [`-printf` is not supported in POSIX `find`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html), which is why I didn't use it. – l0b0 Oct 11 '17 at 06:49
  • 1
    Neither are `-mindepth` and `-maxdepth`, which you used. – Eliah Kagan Oct 11 '17 at 10:34
  • Good point, I hadn't picked up on that! – l0b0 Oct 11 '17 at 18:27
  • BSD `find` contains `-mindepth` and `-maxdepth` but does not contain `-printf` so I do appreciate the inclusion of both. – Joseph Feb 09 '18 at 15:09
  • 2
    fyi, this doesn't work on macOS, "-printf: unknown primary or operator" – Jemshit Jun 09 '22 at 07:46
36

Here you can do this way

ls -lR /path/to/dir/*.jpg | wc -l

This gives you count

sk1712
  • 522
  • 4
  • 9
  • 6
    Why the `-R`? Why doing a full `stat` when you just need the filename? Why not `ls -1 *.jpg| wc -l`? (ok, not working if you have filenames with newlines in it. In that case you deserve it ;-) ...) – Rmano Apr 24 '14 at 16:14
  • This is for listing subdirectories recursively. – sk1712 Apr 24 '14 at 16:19
  • 3
    ...and the question was "counting the number of files in a directory". Moreover, this will list recursively *all content* of subdirectories *which name end in .jpg*, not files in subdirectories ending in `.jpg`. Have you ever tested it? – Rmano Apr 24 '14 at 16:28
  • 1
    The question says "files in a specific directory", which implies that subdirectory recursion is not wanted. – David Richerby Apr 24 '14 at 16:58
  • 2
    This also fails if there are too many files in the directory (because `*.jpg` is expanded by the shell, not by `ls`) and if there are files whose names begin with hyphens. – David Richerby Apr 24 '14 at 18:41
  • 2
    @DavidRicherby This skips names that start with `.`. But it actually works okay with hyphens in names, except when `/path/to/dir` is the empty string or a relative path beginning with a hyphen. But **I was wrong before** to have said `ls` replaces characters with `?`. That happens when stdout is a terminal, but here the output is a pipe so `ls` sends all characters from filenames to `wc`. **That makes this answer wrong without the `-q` or `-b` flag to change that behavior**, because filenames can contain newlines. [l0b0's answer](https://askubuntu.com/a/454568/22949) is free of these problems. – Eliah Kagan Oct 10 '17 at 17:49
  • @sk1712 yes, but how do you put this into a bash script? – KansaiRobot Aug 23 '21 at 15:16
  • Despite all the negative comments, this worked for me, was much easier to follow, and was accepted by the person who originally asked the question. I'm finding it to be quite slow when there's a lot of files, and frustratingly much slower than "ls -1 | wc -l", but at least it works! – user1271772 Aug 14 '23 at 05:34
24

This one finds, sorts, and lists all files by extension in order:

find . -type f | sed 's/.*\.//' | sort | uniq -c
Eric Carvalho
  • 53,609
  • 102
  • 137
  • 162
squozen
  • 241
  • 2
  • 2
13

I think it's very simple as following commands.

$ find . -name "*.mp4" | wc -l
8

or

$ find . | grep -i ".mp4$" | wc -l
8

I think that above commands calculate count of files and directories names *.mp4

so I suggest you use -type f option as find parameter as following.

$ find . -name "*.mp4" -type f | wc -l
8

In addition, ls -lR can be used as find .

xiaodongjie
  • 2,796
  • 1
  • 17
  • 37
3

You could use ls -1 *.mp4 | wc -l.

This will list all files ending on .mp4, printing each file on a new line (ls -1 *.mp4), pipe the output to wc which will count the number of new lines using the -l flag.

Louis Matthijssen
  • 11,755
  • 6
  • 44
  • 50
  • Not sure why this was downvoted, it works – Panther Apr 24 '14 at 16:13
  • Since `*.mp4` is expanded by the shell, not `ls`, this will fail if there are so many `.mp4` files in the directory that the list of them can't be passed to `ls` as arguments. – David Richerby Apr 24 '14 at 17:06
  • @Rmana Test it in a directory containing a file called `--.mp4` – David Richerby Apr 24 '14 at 19:33
  • @DavidRicherby yes, you are right. `--` will solve the second case; the former will rise an error message (you need a lot of files!) and another nice question here. Corner cases, but worth noticing, yes. – Rmano Apr 25 '14 at 12:59
  • 1
    @Rmano Without adding `-q` or `-b`, this does *not* tolerate newlines in filenames. If you run `ls -1 *.mp4` from your terminal with no pipe, `ls` sees its [stdout](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29) is a terminal and takes `-q` as implied, printing `?`s. But that behavior goes away when the output is piped. `ls -1 *.mp4 | wc -l` overcounts if there are newlines. `ls -1 *.mp4 | cat` shows what `wc` "sees." (Similarly, `-1` is implied when stdout *isn't* a terminal, so it's optional.) `ls -1q -- *.mp4 | wc -l` almost works, but not with zero .mp4 files. – Eliah Kagan Oct 11 '17 at 11:42
2

Check How To Count The Files By Extension In Linux?, it gives a good answer and explanation, you can use the following command:

find . -type f | sed -n 's/..*\.//p' | sort | uniq -c
Eliah Kagan
  • 116,445
  • 54
  • 318
  • 493
Dilip Rajkumar
  • 387
  • 3
  • 5
2

You can always just use a for loop, which I think has the advantage of not requiring you to remember the flags of several different commands.

For example:

a=0; for i in ./*.jpg; do a=$(expr  $a + 1); done; echo $a 
zx485
  • 2,249
  • 11
  • 24
  • 34
2

This should give you the list of file with .mp4

ls /path/to/directory | grep ".mp4$"

When combined with wc -l will give you count

ls /path/to/directory | grep ".mp4$" | wc -l

if you want search to include subdirectories

ls -lR /path/to/directory | grep ".mp4$" | wc -l
Back.Slash
  • 2,146
  • 2
  • 16
  • 22
  • does not give a count – Panther Apr 24 '14 at 16:15
  • gave it to me when used with `wc -l` – Back.Slash Apr 24 '14 at 16:16
  • use ls without piping to grep and shorten your answer. It has been posted at least twice now. – Panther Apr 24 '14 at 16:19
  • @bodhi.zazen `ls /directory/*.mp4` causes the shell to expand the glob and execute something like `ls /directory/file1.mp4 /directory/file2.mp4 ...` This will fail if the directory contains more mp4 files than can be passed as arguments to `ls`. – David Richerby Apr 24 '14 at 17:03
  • @DavidRicherby - it is not my ls command , I use find ;) – Panther Apr 24 '14 at 17:14
  • @bodhi.zazen In your comment, you said to "use ls without piping to grep". What did you mean, if you didn't mean `ls -lR /path/*.mp4`? – David Richerby Apr 24 '14 at 18:27
  • @DavidRicherby I mean there is no need to pipe to grep – Panther Apr 24 '14 at 18:40
  • @bodhi.zazen If you do `ls /path/ | wc -l`, you'll count all files, not just the mp4 files (and potentially fail for files containing newlines, depending on the behaviour of `ls`). – David Richerby Apr 24 '14 at 18:43
  • The methods given in this answer are extremely brittle and often count wrong. *[a]* Without `-q` or `-b`, newlines in filenames are piped literally to `grep`, as `-q` is only implied when [stdout](https://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29) is a terminal. (Use `ls /path/to/directory | cat` to check what `grep` sees.) *[b]* `.` in a regex matches *any* character, not just `.`, so names like `abcmp4` are matched. (`\.` or `[.]` would avoid this.) *[c]* `ls -l` shows symlink targets, so you may get `foo -> /path/to/file.mp4` and `foo` will be counted as an `.mp4`. – Eliah Kagan Oct 11 '17 at 12:13
1
ls | grep --count \.csv$

Replace (.csv with the extension you want)

Explanation: I think that a simple scheme is to fetch the list of files, and count the extension with grep. \. to match . and $ to match the extension at the end of line. It works because when the output of ls is piped, one file name is sent per line, which you can verify by running:

ls | cat
galoget
  • 2,943
  • 2
  • 20
  • 24
1

In bash, one cold resort to using arrays with glob:

$ files=( *.mp4  )
$ echo ${#files[@]}
30
Sergiy Kolodyazhnyy
  • 103,293
  • 19
  • 273
  • 492