J'ai un dossier appelé img
, ce dossier contient plusieurs niveaux de sous-dossiers, contenant tous des images. Je vais les importer dans un serveur d'images.
Normalement, les images (ou tous les fichiers) peuvent avoir le même nom si elles se trouvent dans un chemin de répertoire différent ou ont une extension différente. Cependant, le serveur d'images dans lequel je les importe nécessite que tous les noms d'image soient uniques (même si les extensions sont différentes).
Par exemple, les images background.png
et background.gif
ne seraient pas autorisées car, même si elles ont des extensions différentes, elles portent toujours le même nom de fichier. Même s'ils se trouvent dans des sous-dossiers distincts, ils doivent toujours être uniques.
Je me demande donc si je peux effectuer une recherche récursive dans le dossier img
pour trouver une liste de fichiers portant le même nom (sans l'extension).
Est-ce qu'il y a une commande qui peut faire ça?
FSlint est un Finder de doublons polyvalent qui comprend une fonction permettant de rechercher les noms en double:
Le paquetage FSlint pour Ubuntu met l’accent sur l’interface graphique, mais comme l’explique la FAQ de FSlint , une interface de ligne de commande est disponible via les programmes de /usr/share/fslint/fslint/
. Utilisez l'option --help
pour la documentation, par exemple:
$ /usr/share/fslint/fslint/fslint --help
File system lint.
A collection of utilities to find lint on a filesystem.
To get more info on each utility run 'util --help'.
findup -- find DUPlicate files
findnl -- find Name Lint (problems with filenames)
findu8 -- find filenames with invalid utf8 encoding
findbl -- find Bad Links (various problems with symlinks)
findsn -- find Same Name (problems with clashing names)
finded -- find Empty Directories
findid -- find files with dead user IDs
findns -- find Non Stripped executables
findrs -- find Redundant Whitespace in files
findtf -- find Temporary Files
findul -- find possibly Unused Libraries
zipdir -- Reclaim wasted space in ext2 directory entries
$ /usr/share/fslint/fslint/findsn --help
find (files) with duplicate or conflicting names.
Usage: findsn [-A -c -C] [[-r] [-f] paths(s) ...]
If no arguments are supplied the $PATH is searched for any redundant
or conflicting files.
-A reports all aliases (soft and hard links) to files.
If no path(s) specified then the $PATH is searched.
If only path(s) specified then they are checked for duplicate named
files. You can qualify this with -C to ignore case in this search.
Qualifying with -c is more restictive as only files (or directories)
in the same directory whose names differ only in case are reported.
I.E. -c will flag files & directories that will conflict if transfered
to a case insensitive file system. Note if -c or -C specified and
no path(s) specifed the current directory is assumed.
Exemple d'utilisation:
$ /usr/share/fslint/fslint/findsn /usr/share/icons/ > icons-with-duplicate-names.txt
$ head icons-with-duplicate-names.txt
-rw-r--r-- 1 root root 683 2011-04-15 10:31 Humanity-Dark/AUTHORS
-rw-r--r-- 1 root root 683 2011-04-15 10:31 Humanity/AUTHORS
-rw-r--r-- 1 root root 17992 2011-04-15 10:31 Humanity-Dark/COPYING
-rw-r--r-- 1 root root 17992 2011-04-15 10:31 Humanity/COPYING
-rw-r--r-- 1 root root 4776 2011-03-29 08:57 Faenza/apps/16/DC++.xpm
-rw-r--r-- 1 root root 3816 2011-03-29 08:57 Faenza/apps/22/DC++.xpm
-rw-r--r-- 1 root root 4008 2011-03-29 08:57 Faenza/apps/24/DC++.xpm
-rw-r--r-- 1 root root 4456 2011-03-29 08:57 Faenza/apps/32/DC++.xpm
-rw-r--r-- 1 root root 7336 2011-03-29 08:57 Faenza/apps/48/DC++.xpm
-rw-r--r-- 1 root root 918 2011-03-29 09:03 Faenza/apps/16/Thunar.png
find . -mindepth 1 -printf '%h %f\n' | sort -t ' ' -k 2,2 | uniq -f 1 --all-repeated=separate | tr ' ' '/'
Comme le dit le commentaire, cela trouvera également des dossiers. Voici la commande pour le limiter aux fichiers:
find . -mindepth 1 -type f -printf '%p %f\n' | sort -t ' ' -k 2,2 | uniq -f 1 --all-repeated=separate | cut -d' ' -f1
Enregistrez ceci dans un fichier nommé duplicates.py
#!/usr/bin/env python
# Syntax: duplicates.py DIRECTORY
import os, sys
top = sys.argv[1]
d = {}
for root, dirs, files in os.walk(top, topdown=False):
for name in files:
fn = os.path.join(root, name)
basename, extension = os.path.splitext(name)
basename = basename.lower() # ignore case
if basename in d:
print(d[basename])
print(fn)
else:
d[basename] = fn
Ensuite, rendez le fichier exécutable:
chmod +x duplicates.py
Exécuter par exemple comme ça:
./duplicates.py ~/images
Il devrait produire des paires de fichiers qui ont le même nom de base (1). Ecrit en python, vous devriez pouvoir le modifier.
Je suppose que vous n'avez besoin que de voir ces "doublons", puis de les manipuler manuellement. Si oui, ce code bash4 devrait faire ce que vous voulez, je pense.
declare -A array=() dupes=()
while IFS= read -r -d '' file; do
base=${file##*/} base=${base%.*}
if [[ ${array[$base]} ]]; then
dupes[$base]+=" $file"
else
array[$base]=$file
fi
done < <(find /the/dir -type f -print0)
for key in "${!dupes[@]}"; do
echo "$key: ${array[$key]}${dupes[$key]}"
done
Voir http://mywiki.wooledge.org/BashGuide/Arrays#Associative_Arrays et/ou le manuel bash pour obtenir de l'aide sur la syntaxe du tableau associatif.
C'est bname:
#!/bin/bash
#
# find for jpg/png/gif more files of same basename
#
# echo "processing ($1) $2"
bname=$(basename "$1" .$2)
find -name "$bname.jpg" -or -name "$bname.png"
Rendez-le exécutable:
chmod a+x bname
Invoquez-le:
for ext in jpg png jpeg gif tiff; do find -name "*.$ext" -exec ./bname "{}" $ext ";" ; done
Pro:
Con:
Amélioration du script de loevborg, pour mes besoins (inclut une sortie groupée, une liste noire, une sortie plus nette lors de l'analyse). Je numérisais un disque de 10 To, il me fallait donc une sortie un peu plus propre.
Usage:
python duplicates.py DIRNAME
duplicates.py
#!/usr/bin/env python
# Syntax: duplicates.py DIRECTORY
import os
import sys
top = sys.argv[1]
d = {}
file_count = 0
BLACKLIST = [".DS_Store", ]
for root, dirs, files in os.walk(top, topdown=False):
for name in files:
file_count += 1
fn = os.path.join(root, name)
basename, extension = os.path.splitext(name)
# Enable this if you want to ignore case.
# basename = basename.lower()
if basename not in BLACKLIST:
sys.stdout.write(
"Scanning... %s files scanned. Currently looking at ...%s/\r" %
(file_count, root[-50:])
)
if basename in d:
d[basename].append(fn)
else:
d[basename] = [fn, ]
print("\nDone scanning. Here are the duplicates found: ")
for k, v in d.items():
if len(v) > 1:
print("%s (%s):" % (k, len(v)))
for f in v:
print (f)