重複ファイルを見つけて、それらをシンボリックリンクに置き換えます


16

特定のディレクトリ内で重複ファイル(名前が異なる場合でも)をチェックし、それらを最初の出現を指すシンボリックリンクに置き換える方法を見つけようとしています。私は試しましたfdupesが、それらの重複をリストするだけです。
それがコンテキストです:私は自分の好みに合わせてアイコンテーマをカスタマイズしていますが、親フォルダー内の名前や場所が異なっていても、さまざまな目的に使用されていても、多くのアイコンは基本的に同じであることがわかりました画像。1つだけが本当に必要な場合、同じ修正を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スクリプトを作成するのはそれほど難しくないはずですが、自分で作成する方法を見つけるのに十分な専門家ではありません。

回答:


3

最初; 通常のハードリンクではなく、シンボリックリンクを使用する必要がある理由はありますか?相対パスを持つシンボリックリンクの必要性を理解するのに苦労しています。この問題を解決する方法は次のとおりです。

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

-Lオプションのあるバージョンがない場合は、commandlinefuで見つけたこの小さな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ノード番号を持つようになりました。つまり、それらはすべてディスク上の同じ物理データを指します。

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


私はリコールfdupes @arnefm、リンクをdupesを交換するためのオプションを有するが、私は中に何を見ることができないも、それはのオプションですv1.51(Ubuntuの14.04.2 LTS)。
アラステア

github.com/jbruchon/jdupesの私のフォークjdupesには、複製セットの望ましいハードリンクを行うオプションがあります。-L
ジョディ・リーブルション

ここでスクリプトを微調整しました。まだスペースを処理しませんが、他の特殊文字を処理します(ファイルにURLクエリ文字列がありました)。また、この${line//…/}部分は私にとっては機能していなかったため、最初の「マスター」ファイルをハードリンクするためのよりクリーンな方法を実行しました。
IBBoard

1
rsync別の種類のファイルシステムを使用している場合、相対ソフトリンクが必要ですか?または、ファイルシステムが階層を保持しない場合、たとえば、すべてを下に置くバックアップサーバー/«machine-name»/...ですか?または、バックアップから復元したい場合は?ここでは、ハードリンクがどのように保持されるかわかりません。相対ソフトリンクの方が生き残る可能性が高いと思います。
バディ

6

スクリプティングにあまり興味がなければ、rdfindをお勧めします。指定されたディレクトリで重複フ​​ァイルをスキャンし、それらをハードリンクまたはソフトリンクします。Ruby gemsディレクトリの重複排除に使用し、大成功を収めました。Debian / Ubuntuで利用可能です。


4

同様の状況がありましたが、私の場合、シンボリックリンクは相対パスを指す必要があるため、この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)

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


1

したがって、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コマンドがサブシェルで実行される場合、外部ファイル(「ファイル」)は削除できます。サブシェルはwhileによって直接読み取られますが、この方法はより明確に見えます。


2
このバージョンは、パイプを含む名前のファイルを処理しますか?私はどちらのバージョンも改行を含むファイル名を処理しないと思いますが、それは他のものではなくfdupesの制限です。
dhag

(「N」またはそのような何かにIFSが動作するはずです)、それはしませんが、あなたは何でもしたい(また、sedの交換の値を変更)するためにIFSを設定することができ、その後、あなたはどんな問題があってはならない
デヴィッド・ベンチュラ

これにより壊れたシンボリックリンクが作成され、自分自身にリンクされたファイルがあります。使用しないで
ください-MrMesees

0

事前の注意事項:

  • 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

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

これを行ごとに任意の数の重複を自動的に処理するために使うと、もう少し手間がかかります。

別のアプローチは、最初に絶対パスへのシンボリックリンクを作成し、次にそれらを変換することです。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.