重複ファイルを見つけてシンボリックリンクに置き換える

重複ファイルを見つけてシンボリックリンクに置き換える

指定されたディレクトリに重複したファイル(名前が異なる場合でも)があることを確認し、最初のエントリを指すシンボリックリンクに置き換える方法を見つけようとしています。試してみましたが、重複したfdupes項目のみが一覧表示されます。
状況は次のとおりです。アイコンテーマを好きなようにカスタマイズしていますが、親フォルダで名前と場所が異なり、他の目的に使用されるアイコンが多いとしても基本的にすべて同じであることがわかりました。絵。同じ修正を20〜30回適用することは、実際には一度必要なときに重複するので、1つの画像のみを維持し、他の画像はすべてシンボリックリンクしたいと思います。

たとえば、fdupes -r ./ディレクトリで実行すると、testdir次の結果が返されることがあります。

./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スクリプトを作成することはそれほど難しくないと確信していますが、それを自分で書く方法を理解するのに十分な専門家ではありません。

答え1

あまりにも多くのスクリプトが気に入らない場合はお勧めします。探す。指定したディレクトリから重複ファイルを検索し、それをハードリンクまたはシンボリックリンクに置き換えます。私はこれを使ってRuby gemsディレクトリの重複を正常に削除しました。 Debian/Ubuntu で使用できます。

答え2

同様の状況がありましたが、私の場合、シンボリックリンクは相対パスを指す必要があるため、次のように書きました。このPythonスクリプト実現する:

#!/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)

各入力行(ファイルリスト)に対して、スクリプトはファイルリスト(スペースで区切り)を分割し、各ファイルから最初のファイルへの相対パスを取得し、シンボリックリンクを作成します。

答え3

まず、一般的なハードリンクの代わりにシンボリックリンクを使用する理由はありますか?相対パスを持つシンボリックリンクの必要性を理解するのが困難です。この問題を解決した方法は次のとおりです。

私はfdupesのUbuntuバージョンがこの-Lオプションを使用して冗長リンクをハードリンクに置き換えることができると思いますが、これを確認するためのDebianインストールはありません。

そのオプションを含むバージョンがない場合は、-L私が見つけたこの小さな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

上記のコマンドは、「パス」ですべての重複ファイルを見つけてハードリンクに置き換えます。実行し、inode番号を見ると、ls -ilRこれを確認できます。以下は、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

すべてのファイルには別々のinode番号があるので、別々のファイルになります。それでは重複を取り除きましょう。

$ 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

今、これらのファイルはすべて同じinode番号を持ちます。これはすべてディスク上の同じ物理データを指すことを意味します。

これがあなたの問題を解決するか、少なくとも正しい方向を提示することを願っています。

答え4

いくつかの注意:

  • BASH固有
  • ファイル名にスペースがありません。
  • 各行には最大2つのファイルが含まれているとします。

fdupes -1r common/base/dir | while read -r -a line ; do ln -sf $(realpath --relative-to ${line[1]} ${line[0]}) ${line[1]}; done

複数のファイルが重複している場合(例: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

関連情報