I downloaded a lot of images in a directory.
Downloader renamed files which already exist.
I also renamed some of the files manually.

hello.jpg      <-- manually renamed `b(3).jpg`
world.jpg      <-- manually renamed `d.jpg`

How to remove duplicated ones? The result should be:


note: name doesn't matter. I just want uniq files.

16 Answers 16


bash 4.x

declare -A arr
shopt -s globstar

for file in **; do
  [[ -f "$file" ]] || continue
  read cksm _ < <(md5sum "$file")
  if ((arr[$cksm]++)); then 
    echo "rm $file"

This is both recursive and handles any file name. Downside is that it requires version 4.x for the ability to use associative arrays and recursive searching. Remove the echo if you like the results.

gawk version

gawk '
    cmd="md5sum " q FILENAME q
    cmd | getline cksm
    sub(/ .*$/,"",cksm)
      cmd="echo rm " q FILENAME q
  }' q='"' *

Note that this will still break on files that have double-quotes in their name. No real way to get around that with awk. Remove the echo if you like the results.

  • fine, the bash version worked for me, but in my test, with 2 similar folders, it deleted half of duplicates in one folder, and half in the other. why. i would expect deletion of everyone (duplicated) of one folder.
    – Ferroao
    Commented Dec 5, 2017 at 17:58
  • @Ferroao Perhaps they were not exact duplicates. If just one bit is off the md5 hash that my script is using to determine duplicity would be completely different. You could add an echo cksm just after the line starting with read if you want to see each file’s hash.
    – SiegeX
    Commented Dec 5, 2017 at 20:38
  • no, all "duplicates" (copies) were removed, remaining 1 version, let's say the original. half copies were deleted from one folder, and the other half from the other folder (100% deletion of copies). my 100% is for copies in excess, not of the totality
    – Ferroao
    Commented Dec 5, 2017 at 22:40
  • @Ferroao I see. In that case it seems when bash does its recursive path expansion via **, it orders the list in such a way that the two folders are interleaved rather than all of folder 1 then all of folder 2. The script will always leave the first ‘original’ it hits as it iterates through the list. You can echo $file before the read line to see if this is true.
    – SiegeX
    Commented Dec 6, 2017 at 0:49
  • Don't reinvent the wheel. This is a kind of dangerous operation and you really don't want to use a program written by someone while browsing stackexchange during a boring meeting.
    – Nobody
    Commented Mar 5, 2020 at 11:57

fdupes is the tool of your choice. To find all duplicate files (by content, not by name) in the current directory:

fdupes -r .

To manually confirm deletion of duplicated files:

fdupes -r -d .

To automatically delete all copies but the first of each duplicated file (be warned, this warning, this actually deletes files, as requested):

fdupes -r -f . | grep -v '^$' | xargs rm -v

I'd recommend to manually check files before deletion:

fdupes -rf . | grep -v '^$' > files
... # check files
xargs -a files rm -v
  • Works great, but fails if file names contain spaces. Commented Jun 23, 2017 at 12:15
  • 2
    @DanielWolf try with xargs option -d '\n'
    – Jakob
    Commented Jun 27, 2017 at 12:13
  • 9
    Also, newer versions of fdupes have the built-in option to delete all but the first in a list of duplicate files: fdupes -rdN . where -r is recursive, -d is delete and -N is no-prompt
    – Rand
    Commented May 15, 2019 at 22:38
  • Thank you, This is outstanding because can detect more than 2 duplicates and allows you to select which one of the dups you want to preserve (or all of them).
    – Smeterlink
    Commented Aug 4, 2019 at 16:39
  • xargs: unterminated quote
    – Alexey Sh.
    Commented Jan 3, 2020 at 5:53

I recommend fclones.

Fclones is a modern duplicate file finder and remover written in Rust, available on most Linux distros and macOS.

Notable features:

  • supports spaces, non-ASCII and control characters in file paths
  • allows to search in multiple directory trees
  • respects .gitignore files
  • safe: allows to inspect the list of duplicates manually before performing any action on them
  • offers plenty of options for filtering / selecting files to remove or preserve
  • very fast

To search for duplicates in the current directory simply run:

fclones group . >dupes.txt

Then you can inspect the dupes.txt file to check if it found the right duplicates (you can also modify that list to your liking).

Finally remove/link/move the duplicate files with one of:

fclones remove <dupes.txt
fclones link <dupes.txt
fclones move target <dupes.txt
fclones dedupe <dupes.txt   # copy-on-write deduplication on some filesystems


pkolaczk@p5520:~/Temp$ mkdir files
pkolaczk@p5520:~/Temp$ echo foo >files/foo1.txt
pkolaczk@p5520:~/Temp$ echo foo >files/foo2.txt
pkolaczk@p5520:~/Temp$ echo foo >files/foo3.txt

pkolaczk@p5520:~/Temp$ fclones group files >dupes.txt
[2022-05-13 18:48:25.608] fclones:  info: Started grouping
[2022-05-13 18:48:25.613] fclones:  info: Scanned 4 file entries
[2022-05-13 18:48:25.613] fclones:  info: Found 3 (12 B) files matching selection criteria
[2022-05-13 18:48:25.614] fclones:  info: Found 2 (8 B) candidates after grouping by size
[2022-05-13 18:48:25.614] fclones:  info: Found 2 (8 B) candidates after grouping by paths and file identifiers
[2022-05-13 18:48:25.619] fclones:  info: Found 2 (8 B) candidates after grouping by prefix
[2022-05-13 18:48:25.620] fclones:  info: Found 2 (8 B) candidates after grouping by suffix
[2022-05-13 18:48:25.620] fclones:  info: Found 2 (8 B) redundant files

pkolaczk@p5520:~/Temp$ cat dupes.txt
# Report by fclones 0.24.0
# Timestamp: 2022-05-13 18:48:25.621 +0200
# Command: fclones group files
# Base dir: /home/pkolaczk/Temp
# Total: 12 B (12 B) in 3 files in 1 groups
# Redundant: 8 B (8 B) in 2 files
# Missing: 0 B (0 B) in 0 files
6109f093b3fd5eb1060989c990d1226f, 4 B (4 B) * 3:

pkolaczk@p5520:~/Temp$ fclones remove <dupes.txt
[2022-05-13 18:48:41.002] fclones:  info: Started deduplicating
[2022-05-13 18:48:41.003] fclones:  info: Processed 2 files and reclaimed 8 B space

pkolaczk@p5520:~/Temp$ ls files
  • Great! Another addition to my best-in-class tool arsenal in Rust. Commented Feb 12, 2023 at 20:21

You can try FSLint. It has both command line and GUI interface.


How to test files having unique content?

if diff "$file1" "$file2" > /dev/null; then

How can we get list of files in directory?

files="$( find ${files_dir} -type f )"

We can get any 2 files from that list and check if their names are different and content are same.

# removeDuplicates.sh

if [[ -z "$files_dir" ]]; then
    echo "Error: files dir is undefined"

files="$( find ${files_dir} -type f )"
for file1 in $files; do
    for file2 in $files; do
        # echo "checking $file1 and $file2"
        if [[ "$file1" != "$file2" && -e "$file1" && -e "$file2" ]]; then
            if diff "$file1" "$file2" > /dev/null; then
                echo "$file1 and $file2 are duplicates"
                rm -v "$file2"

For example, we have some dir:

$> ls .tmp -1

So there are only 3 unique files.

Lets run that script:

$> ./removeDuplicates.sh .tmp/
.tmp/text(2) and .tmp/text are duplicates
removed `.tmp/text'
.tmp/all.txt and .tmp/all(2).txt are duplicates
removed `.tmp/all(2).txt'

And we get only 3 files leaved.

$> ls .tmp/ -1

I wrote this tiny script to delete duplicated files


Basically it uses a temporary file (/tmp/list.txt) to create a map of files and their hashes. Later I use that files and the magic of Unix pipes to do the rest.

The script won't delete anything but will print the commands to delete files.

mfilter.sh ./dir | bash

Hope it helps


More concise version of removing duplicated files(just one line)

young@ubuntu-16:~/test$ md5sum `find ./ -type f` | sort -k1 | uniq -w32 -d | xargs rm -fv


#!/usr/bin/env bash
#set -x
#This is small script can find same size of files.

if [[ -z $1 || ! -d $1 ]]
echo "Usage $0 directory_name" ;
 exit $?
echo "current directory is $1"

for i in $(find $dir_name -type f); do
   ls -fl $i
done | awk '{f=""
        if(NF>9)for(i=9;i<=NF;i++)f=f?f" "$i:$i; else f=$9;
        if(a[$5]){ a[$5]=a[$5]"\n"f; b[$5]++;} else a[$5]=f} END{for(x     in b)print a[x] }' | xargs stat -c "%s  %n" #For just list files

find_same_size $1

young@ubuntu-16:~/test$ bash find_same_size.sh tttt/ | awk '{ if($1 !~   /^([[:alpha:]])+/) print $2}' | xargs md5sum | uniq -w32 -d | xargs rm -vf

Here is one-liners (based on answer from Prashant Lakhera).


find . -type f | xargs -I {} md5sum "{}" | sort -k1 | uniq -w32 -d | cut -d" " -f2- | xargs -I {} echo "{}"


find . -type f | xargs -I {} md5sum "{}" | sort -k1 | uniq -w32 -d | cut -d" " -f2- | xargs -I {} rm -f "{}"

And here is slightly more complex version, that tries to preserve files that resides deeper in directory tree and have longer filenames. Presumably this files have been sorted manually.

find . -type f | xargs -I {} md5sum "{}" | awk '{print gsub("/","/",$0), length, $0}' | sort -k3,3 -k2,2n -k1,1n | cut -d" " -f3- | uniq -w32 -d | cut -d" " -f2- | xargs -I {} rm -f "{}"

Drawback: if you have more than 2 copies of a file you have to launch command multiple times.

  • In which way is this better than other answers?
    – Toto
    Commented Oct 12, 2022 at 11:29

I found an easier way to perform the same task

for i in `md5sum * | sort -k1 | uniq -w32 -d|awk '{print $2}'`; do
rm -rf $i

This is not what you are asking, but I think someone might find it useful when the checksums are not the same, but the name is similar (with suffix in parentheses). This script removes the files with suffixes as ("digit")

#! /bin/bash
# Warning: globstar excludes hidden directories.
# Turn on recursive globbing (in this script) or exit if the option is not supported:
shopt -s globstar || exit
for f in **
#get only files with parentheses suffix
FILEWITHPAR=$( echo "${f%.*}".$extension | grep -o -P "(.*\([0-9]\)\..*)")
# print file to be possibly deleted
if [ -z "$FILEWITHPAR" ] ;then
echo "$FILEWITHPAR ident"
# identify if a similar file without suffix exists
FILENOPAR=$(echo $FILEWITHPAR | sed -e 's/^\(.*\)([0-9])\(.*\).*/\1\2/')
echo "$FILENOPAR exists?"
if [ -f "$FILENOPAR" ]; then
#delete file with suffix in parentheses
echo ""$FILEWITHPAR" to be deleted"
echo "no"

Most and possibly all of the remaining answers are terribly inefficient by computing the checksum of each and every file in the directory to process.

A potentially orders of magnitude faster approach is to first get the size of each file, which is almost immediate (ls or stat), and then compute and compare the checksums only for the files having a non unique size and only keep one instance of the files sharing both their size and checksum.

Note that even though theoretically hash collisions can occur, there are not enough jpeg files on the entire Internet for a hash collision to reasonably have a chance to happen. Two files sharing both their size and checksum are identical for all intents and purposes.

See: How reliable are SHA1 sum and MD5 sums on very large files?

  • Yes, and you should not take a decision on the checksum only. It denotes a very high probability of content equality, but no more. So, if there is only two files of same size to be compared, it is useless to compute the checksum because in all cases you will need to compare them bit by bit to certify the equality. Commented Jan 14, 2021 at 16:19
  • @LaurentSimon Yes, that's what my answer is telling.
    – jlliagre
    Commented Jan 14, 2021 at 16:26
  • @jliagre, no, your answer is saying that a checksum is enough to identify duplicates. If you can't tolerate false positives, you have to compare bit by bit, like Laurent pointed out. Commented Feb 27, 2021 at 17:12
  • @DominykasMostauskis You are right, I'm telling a checksum is enough to identify duplicates. Comparing bit by bit can be done but is mostly pointless, unless there are hundred of billions of files in that directory. Answer updated.
    – jlliagre
    Commented Feb 28, 2021 at 0:01
  • @LaurentSimon Certifying the files are identical by comparing them bit by bit can of course be done but is essentially pointless. An accidental hash collision has certainly much less chance to occur than an accidental bit corruption anyway.
    – jlliagre
    Commented Feb 28, 2021 at 12:46

There is a beautiful solution on https://stackoverflow.com/questions/57736996/how-to-remove-duplicate-files-in-linux/57737192#57737192:

md5sum prime-* | awk 'n[$1]++' | cut -d " " -f 3- | xargs rm

Another very clear and nice solution is mentioned on https://unix.stackexchange.com/questions/192701/how-to-remove-duplicate-files-using-bash:

md5sum * | sort -k1 | uniq -w 32 -d

Here is an alternate version which runs on Mac , example to filter set to *.png


md5sum *.png | sort | awk 'BEGIN { val=$1} { if ($1 == val) print $2; val=$1}'


 md5sum *.png | sort | awk 'BEGIN { val=$1} { if ($1 == val) print $2; val=$1}' | xargs -I X rm -f "X"

Examples of creating aliases for csh/tcsh

alias lsdup "md5sum \!:1 | sort | awk 'BEGIN { val="\$"1} { if ( "\$"1 == val ) print "\$"2 ; val="\$"1}'"
alias rmdup "md5sum \!:1 | sort | awk 'BEGIN { val="\$"1} { if ( "\$"1 == val ) print "\$"2 ; val="\$"1}'| xargs -I X rm -fv X"

try cldup, https://github.com/jkzhang2019/cldup

I have images of almost 1TB that collected over ten years. To remove duplicate files I created this project and wit works well.

With cldup you can create a database of all your files and identify any duplicate file with one line of command.

The database can be maintained increasingly, so once a database was created, you can updated it in minutes.

  • btw, It's a bash script. Commented Jun 15, 2023 at 1:35
  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review Commented Jun 15, 2023 at 2:20

Deduplicator - Find, Sort, Filter & Delete duplicate files


# Scan for duplicates recursively from the current dir, only look for png, jpg & pdf file types & interactively delete files
deduplicator -t pdf,jpg,png -i

# Scan for duplicates recursively from the ~/Pictures dir, only look for png, jpeg, jpg & pdf file types & interactively delete files
deduplicator ~/Pictures/ -t png,jpeg,jpg,pdf -i

# Scan for duplicates in the ~/Pictures without recursing into subdirectories
deduplicator ~/Pictures --max-depth 0

# look for duplicates in the ~/.config directory while also recursing into symbolic link paths
deduplicator ~/.config --follow-links

# scan for duplicates that are greater than 100mb in the ~/Media directory
deduplicator ~/Media --min-size 100mb

I found a small program that really simplifies this kind of tasks: fdupes.

  • Please add installation instructions and a usage example appropriate for the question.
    – simlev
    Commented Sep 21, 2018 at 8:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .