特定のディレクトリ内で重複したファイル(異なる名前であっても)をチェックし、それらを最初の出現を指すシンボリックリンクで置き換える方法を見つけようとしています。私はfdupes
を試してみましたが、重複を一覧表示するだけです。
それがコンテキストです。アイコンのテーマを自分の好みに合わせてカスタマイズしています。親フォルダー内の名前や場所が異なり、さまざまな目的で使用されている場合でも、多くのアイコンが見つかりました。基本的に同じ画像です。同じ変更を20回または30回適用することは、1つだけが本当に必要な場合は冗長であるため、1つの画像のみを保持し、他のすべての画像にシンボリックリンクを設定します。
例として、testdir
ディレクトリ内でfdupes -r ./
を実行すると、次の結果が返されることがあります。
./file1.png
./file2.png
./subdir1/anotherfile.png
./subdir1/subdir2/yetanotherfile.png
この出力を前提として、ファイルfile1.png
のみを保持し、他のすべてを削除して、元のファイル名をすべて維持しながら、それらを指すシンボリックリンクに置き換えます。したがって、file2.png
はその名前を保持しますが、重複するのではなくfile1.png
へのリンクになります。
これらのリンクは、絶対パスを指すのではなく、親のtestdir
ディレクトリーからの相対パスでなければなりません。つまり、yetanotherfile.png
は../../file1.png
ではなく/home/testuser/.icons/testdir/file1.png
を指します。
GUIとCLIを含むソリューションの両方に興味があります。 fdupes
の使用は必須ではありません。これは私が知っているツールであるため引用しましたが、他のツールを使用するソリューションにも対応できます。
これらすべてを処理するbashスクリプトを作成するのはそれほど難しくないはずですが、自分で書く方法を見つけるのに十分な専門知識はありません。
最初;通常のハードリンクではなくシンボリックリンクを使用する必要がある理由はありますか?相対パスを持つシンボリックリンクの必要性を理解するのに苦労しています。これが私がこの問題をどのように解決するかです:
Debian(Ubuntu)バージョンのfdupesは-L
オプションを使用して重複をハードリンクに置き換えることができると思いますが、これを確認するためのDebianインストールがありません。
-L
オプション付きのバージョンがない場合は、 commandlinef で見つけたこの小さなbashスクリプトを使用できます。
この構文はbashでのみ機能することに注意してください。
fdupes -r -1 path | while read line; do master=""; for file in ${line[*]}; do if [ "x${master}" == "x" ]; then master=$file; else ln -f "${master}" "${file}"; fi; done; done
上記のコマンドは、「パス」内のすべての重複ファイルを見つけて、ハードリンクに置き換えます。これを確認するには、ls -ilR
を実行してiノード番号を確認します。これは、10個の同一ファイルを持つサンプルです。
$ ls -ilR
total 20
3094308 -rw------- 1 username group 5 Sep 14 17:21 file
3094311 -rw------- 1 username group 5 Sep 14 17:21 file2
3094312 -rw------- 1 username group 5 Sep 14 17:21 file3
3094313 -rw------- 1 username group 5 Sep 14 17:21 file4
3094314 -rw------- 1 username group 5 Sep 14 17:21 file5
3094315 drwx------ 1 username group 48 Sep 14 17:22 subdirectory
./subdirectory:
total 20
3094316 -rw------- 1 username group 5 Sep 14 17:22 file
3094332 -rw------- 1 username group 5 Sep 14 17:22 file2
3094345 -rw------- 1 username group 5 Sep 14 17:22 file3
3094346 -rw------- 1 username group 5 Sep 14 17:22 file4
3094347 -rw------- 1 username group 5 Sep 14 17:22 file5
すべてのファイルには個別のiノード番号があり、個別のファイルになります。次に、それらを重複排除します。
$ fdupes -r -1 . | while read line; do j="0"; for file in ${line[*]}; do if [ "$j" == "0" ]; then j="1"; else ln -f ${line// .*/} $file; fi; done; done
$ ls -ilR
.:
total 20
3094308 -rw------- 10 username group 5 Sep 14 17:21 file
3094308 -rw------- 10 username group 5 Sep 14 17:21 file2
3094308 -rw------- 10 username group 5 Sep 14 17:21 file3
3094308 -rw------- 10 username group 5 Sep 14 17:21 file4
3094308 -rw------- 10 username group 5 Sep 14 17:21 file5
3094315 drwx------ 1 username group 48 Sep 14 17:24 subdirectory
./subdirectory:
total 20
3094308 -rw------- 10 username group 5 Sep 14 17:21 file
3094308 -rw------- 10 username group 5 Sep 14 17:21 file2
3094308 -rw------- 10 username group 5 Sep 14 17:21 file3
3094308 -rw------- 10 username group 5 Sep 14 17:21 file4
3094308 -rw------- 10 username group 5 Sep 14 17:21 file5
これで、ファイルのiノード番号はすべて同じになりました。つまり、ファイルはすべてディスク上の同じ物理データを指しています。
これがあなたの問題を解決するか、少なくともあなたが正しい方向に向かっていることを願っています!
スクリプティングをあまり好きではない場合は、 rdfind をお勧めします。指定されたディレクトリをスキャンして重複ファイルを探し、それらをハードリンクまたはソフトリンクします。私はこれを使用して、私のRuby gemsディレクトリの重複排除に大きな成功を収めました。Debian/ Ubuntuで利用可能です。
私も同様の状況でしたが、私の場合、シンボリックリンクは相対パスを指す必要があるため、トリックを行うために this python script と記述しました:
#!/usr/bin/env python
# Reads fdupes(-r -1) output and create relative symbolic links for each duplicate
# usage: fdupes -r1 . | ./lndupes.py
import os
from os.path import dirname, relpath, basename, join
import sys
lines = sys.stdin.readlines()
for line in lines:
files = line.strip().split(' ')
first = files[0]
print "First: %s "% first
for dup in files[1:]:
rel = os.path.relpath(dirname(first), dirname(dup))
print "Linking duplicate: %s to %s" % (dup, join(rel,basename(first)))
os.unlink(dup)
os.symlink(join(rel,basename(first)), dup)
各入力行(ファイルのリスト)に対して、スクリプトはファイルリストを(空白で区切られた)分割し、各ファイルから最初のファイルへの相対パスを取得して、シンボリックリンクを作成します。
したがって、arnefmによって与えられた答え(インターネット全体にコピーされたもの)は、ファイル名のスペースを扱いません。ファイル内のスペースを処理するスクリプトを作成しました。
#!/bin/bash
fdupes -r -1 CHANGE_THIS_PATH | sed -e 's/\(\w\) /\1|/g' -e 's/|$//' > files
while read line; do
IFS='|' read -a arr <<< "$line"
orig=${arr[0]}
for ((i = 1; i < ${#arr[@]}; i++)); do
file="${arr[$i]}"
ln -sf "$orig" "$file"
done
done < files
これが何をするかは、だれかを見つけ、それらを 'files'という名前のファイルに分けられたPIPEに書き込みます。
次に、ファイルを1行ずつ配列に読み込み、配列の各要素はPIPEで区切られます。
次に、配列の最初以外のすべての要素を反復処理し、ファイルを最初の要素へのシンボリックリンクに置き換えます。
Fdupesコマンドがサブシェルで実行される場合、外部ファイル( 'files')を削除できます。これは、whileによって直接読み取られますが、この方法はより明確に見えます。
事前の注意事項:
fdupes -1r common/base/dir | while read -r -a line ; do ln -sf $(realpath --relative-to ${line[1]} ${line[0]}) ${line[1]}; done
3つ以上のファイルが重複している場合(例:file1 file2 file3)、各ペアのシンボリックリンクを作成する必要があります-file1、file2およびfile1、file3を2つの別々のケースとして扱います。
if [[ ${#line[@]} -gt 2 ]] ;then
ln -sf $(realpath --relative-to ${line[1]} ${line[0]}) ${line[1]}
ln -sf $(realpath --relative-to ${line[2]} ${line[0]}) ${line[2]}
...
fi
1行あたりの任意の数の重複を自動的に処理するためにこれを使用すると、もう少し手間がかかります。
別のアプローチは、最初に絶対パスへのシンボリックリンクを作成し、次にそれらを変換することです:
fdupes -1r /absolute/path/common/base/dir | while read -r -a line ; do ln -sf ${line[0]} ${line[1]}; done
chroot /absolute/path/common/base/dir ; symlinks -cr .
これは@Gillesの回答に基づいています: https://unix.stackexchange.com/a/100955/77319