省サイズ技法の開発

現状

まず、「=」は同名のファイルやディレクトリ配下の内容が等しいことを表す。
一方、「≠」は同名のファイルやディレクトリ配下の内容が等しくはないことを表す。
2008
└ A		= ‥/2026/A	ファイルがそれと等しい
└ B/	= ‥/2026/B/	ディレクトリ配下がそれと等しい
└ C		≠ ‥/2026/C	ファイルがそれと等しくない
└ D/	≠ ‥/2026/D/	ディレクトリ配下が一部それと等しくない
  └ 1	= ‥/2026/D/1	ディレクトリ配下のファイルがそれと等しい
  └ 2	≠ ‥/2026/D/2	ディレクトリ配下のファイルがそれと等しくない

2009	同上
└ A		= ‥/2026/A
└ B/	= ‥/2026/B/
└ C		≠ ‥/2026/C
└ D/	≠ ‥/2026/D/
  └ 1	= ‥/2026/D/1 
  └ 2	≠ ‥/2026/D/2

	(中略)

2026	最新
└ A
└ B/
└ C
└ D/
  └ 1
  └ 2

修正

以下のように、「→」は同名のファイルへのシンボリックリンクを表す。
そして、「=」「≠」は同名のファイルやディレクトリ配下の実体を表す。
また、そのようなシンボリックリンクを施すシェルスクリプトとなる。
2008
└ A		→ ‥/2026/A	ファイルがそれと等しい、のでシンボリックリンク
└ B/	→ ‥/2026/B/	ディレクトリ配下がそれと等しい、のでシンボリックリンク
└ C		≠ ‥/2026/C	ファイルがそれと等しくない
└ D/	≠ ‥/2026/D/	ディレクトリ配下が一部それと等しくない
  └ 1	= ‥/2026/D/1	ディレクトリ配下のファイルがそれと等しい、が、シンボリックリンクしない
  └ 2	≠ ‥/2026/D/2	ディレクトリ配下のファイルがそれと等しくない

2009	同上
└ A		→ ‥/2026/A
└ B/	→ ‥/2026/B/
└ C		≠ ‥/2026/C
└ D/	≠ ‥/2026/D/
  └ 1	= ‥/2026/D/1 
  └ 2	≠ ‥/2026/D/2

	(中略)

2026	最新
└ A
└ B/
└ C
└ D/
  └ 1
  └ 2

拡張

次に、2027 ディレクトリが rsync で増えたとする。
2008	2027 以外は、同上
└ A		→ ‥/2026/A
└ B/	→ ‥/2026/B/
└ C		≠ ‥/2026/C
└ D/	≠ ‥/2026/D/
  └ 1	= ‥/2026/1 
  └ 2	≠ ‥/2026/2

2009
└ A		→ ‥/2026/A
└ B/	→ ‥/2026/B/
└ C		≠ ‥/2026/C
└ D/	≠ ‥/2026/D/
  └ 1	= ‥/2026/1 
  └ 2	≠ ‥/2026/2

	(中略)

2026
└ A		= ‥/2027/A
└ B/	= ‥/2027/B/
└ C		≠ ‥/2027/C
└ D/	≠ ‥/2027/D/
  └ 1	=  ‥/2027/D/1 
  └ 2	≠  ‥/2027/D/2

2027	最新
└ A
└ B/
└ C
└ D/
  └ 1
  └ 2

拡張への対応

以下のように、「→」は同名のファイルへのシンボリックリンクを表す。
また、「」のシンボリックリンクは現状では実現できていない技術である。
また、そのようなシンボリックリンクを施すシェルスクリプトとなる。
2008
└ A		→ ‥/2027/A
└ B/	→ ‥/2027/B/
└ C		≠ ‥/2027/C
└ D/	≠ ‥/2027/D/
  └ 1	= ‥/2027/D/1	→ ‥/2027/D/1
  └ 2	≠ ‥/2027/D/2

2009
└ A		→ ‥/2027/A
└ B/	→ ‥/2027/B/
└ C		≠ ‥/2027/C
└ D/	≠ ‥/2027/D/
  └ 1	= ‥/2027/D/1	
  └ 2	≠ ‥/2027/D/2

	(中略)

2026
└ A		→ ‥/2027/A
└ B/	→ ‥/2027/B/
└ C		→ ‥/2027/C
└ D/	≠ ‥/2027/D/	
  └ 1	= ‥/2027/D/1	→ ‥/2027/D/1
  └ 2	≠ ‥/2027/D/2

2027	最新
└ A
└ B/
└ C
└ D/
  └ 1
  └ 2
よって、この省サイズ技法によりストレージが浪費が避けられた。
具体例の一つとして、5Gb が 1Gb 程度になる。

実現 … 引数と先頭と、末尾の引数と比較して等しいならシンボリックリンク、そして、先頭をカットして繰り返し

以下がそのシェルスクリプト

#!/bin/ksh
md5d() {
    [ -d "$1" ] && {
	find "$1" -type f -exec md5 {} \; | sort
    } || {
	md5 "$1"
    }
}
tac() {
    tail -r
}
set -- $(printf "%s\n" "$@" | tac)
base="$1"

while [ "$2" != '' ]; do
    for p in "$base"/*; do
	b="$(basename "$p")"
	if [[ -e "$2/$b" ]]; then
	    diff -q <(cd "$base" && md5d "$b") <(cd "$2" && md5d "$b") > /dev/null && {
		echo "$base/$b" "$2/$b" matched
		(cd "$2" &&
		     rm -rf "$b" &&
		     ln -s ../"$base/$b" .
		)
	    } || {
		:; # echo "$base/$b" and "$2/$b" differ
	    }
	else
	    :; # echo "$2/$b" not found
	fi
    done
    shift
done

実現 … 末尾の引数をカットして、前節の実行、そしてこれを繰り返し

さらに、2008 〜 2017 が等しく、2018 で更新され、2018 〜 2027 が等しいという場合が考えられる。

以下がそのシェルスクリプト

#!/bin/ksh
tac() {
    tail -r
}
basedir="$(dirname "$0")"
while [ "$2"  != '' ]; do
    echo "$basedir"/Symbolic-Link.sh "$@"
    "$basedir"/Symbolic-Link.sh "$@"
    set -- $(printf "%s\n" "$@" | tac)
    shift
    set -- $(printf "%s\n" "$@" | tac)
done

内部で前者のシェルスクリプトを使用している。

Copyleft 🄯 2026 Taiji Yamada