これは、このテキストファイル(ユーティリティの出力)をfslint
一連の規則に従って削除する重複ファイルのコマンドラインと、アーカイブするファイルのコメント行を含むbashスクリプトに変換することです。rm
ルールは基本的に次のとおりです。特定のディレクトリの重複ファイルのみを削除します。
目標は、複数のオペレーティングシステム(Mac OS X、Windows、Linux)で長年蓄積された約1TBの冗長性をクリーンアップすることです。すべてのデータがLinuxドライブにコピーされました。
#3 x 697,612,024 (1,395,236,864) bytes wasted
/path/to/backup-100425/file_a.dat
/another/path/to/backup-disk-name/171023/file_a.dat
/yet/another/path/to/labs data/some/path/file_a.dat
#4 x 97,874,344 (293,634,048) bytes wasted
/path/to/backup-100425/file b.mov
/another/path/to/backup-140102/file b.mov
/backup-120708/Library/some/path/file b.mov
/some/other/path/to/backup-current/file b.mov
#2 x 198,315,112 (198,316,032) bytes wasted
/path/to/backup-100425/file_c.out
/another/path/to/backup-disk-name/171023/file_c.out
最初の行は3つの同じコピーがあることを示し、file_a.dat
次の3行はそのパスをリストします。理想的には、ここから2つのコピーを削除する必要があります。私が言うのは、6桁(YYMMDD形式の日付)で構成されたディレクトリです。履歴バックアップディレクトリ。
ルール、適用この順に同じファイルの各セットは次のとおりです。
- ファイルがディレクトリを含むパスにある場合は、
Library
そのディレクトリを保持してください。 - ファイル
labs data
がまたはにある場合は、backup-current
ファイルを保持し、その中のすべての重複エントリを削除します。履歴バックアップディレクトリ。 - ファイルが履歴バックアップディレクトリにある場合は、ファイルを最新のバックアップディレクトリに保存し、古い重複エントリを削除します。
- それ以外の場合はファイルをアーカイブします。
希望の出力は次のとおりです。
#!/bin/bash
#3 x 697,612,024 (1,395,236,864) bytes wasted
rm '/path/to/backup-100425/file_a.dat'
rm '/another/path/to/backup-disk-name/171023/file_a.dat'
#/yet/another/path/to/labs data/some/path/file_a.dat
#4 x 97,874,344 (293,634,048) bytes wasted
rm '/path/to/backup-100425/file b.mov'
rm '/another/path/to/backup-140102/file b.mov'
#/backup-120708/Library/some/path/file b.mov
#/some/other/path/to/backup-current/file b.mov
#2 x 198,315,112 (198,316,032) bytes wasted
rm '/path/to/backup-100425/file_c.out'
#/another/path/to/backup-disk-name/171023/file_c.out
私はシェルツールawk、grep、sedに慣れていません。このスレッド私は私の最初の草案が概念的に間違っていたことに気づきました。 「それは[私が] Cのような命令型言語でやるべきことを素直に翻訳したものでした。」
実際、ここで扱っている内容は次のとおりです。文書しかし、一緒にファイルの内容。
この状況に適したシェルスクリプトを使用していますか?
それでは、効率的なスクリプトはどのようなものでしょうか?
編集する:@Edの答えとコードを読んだ後、仕事と要件を明確にしようとしました。これは問題を完全に解決しました。
答え1
私がどれくらいの時間を費やすかを考えると、あなたの要件のリストを理解することはできません。ただし、以下はあなたが興味を持っているように見えるファイル形式をソートして印刷するためのスクリプトです。残りはあなたが理解できることを願っています。
$ cat tst.awk
/^#/ { prt(); print; next }
{ files[$0] }
END { prt() }
function prt( file, isLibrary, isLabsBack, isNothing) {
for (file in files) {
if ( file ~ /(^|\/)Library(\/|$)/ ) {
isLibrary[file]
}
else if ( file ~ /(^|\/)(labs data|backup-current)(\/|$)/ ) {
isLabsBack[file]
}
else {
isNothing[file]
}
}
for (file in isLibrary) {
print "Library", file
}
for (file in isLabsBack) {
print "LabsBack", file
}
for (file in isNothing) {
print "Nothing", file
}
delete files
}
。
$ awk -f tst.awk file
#3 x 697,612,024 (1,395,236,864) bytes wasted
LabsBack /yet/another/path/to/labs data/some/path/file_a.dat
Nothing /another/path/to/backup-disk-name/171023/file_a.dat
Nothing /path/to/backup-100425/file_a.dat
#4 x 97,874,344 (293,634,048) bytes wasted
Library /backup-120708/Library/some/path/file b.mov
LabsBack /some/other/path/to/backup-current/file b.mov
Nothing /path/to/backup-100425/file b.mov
Nothing /another/path/to/backup-140102/file b.mov
#2 x 198,315,112 (198,316,032) bytes wasted
Nothing /path/to/backup-100425/file_c.out
Nothing /another/path/to/backup-disk-name/171023/file_c.out
答え2
興味のある人のために質問に記載されている目的の出力を提供するコードは次のとおりです。これは@Edの本当にスマートなコードを少しだけ適用したものです。
BEGIN { print "#!/bin/bash" }
/^#/ { prt(); print; next }
{ files[$0] }
END { prt() }
function prt( file, isDate, isKeep, isDelete, backup, latest, pats) {
# file exists in a current backup directory (yes|no)
backup = "no"
# latest historical backup date
latest = "000000"
for (file in files) {
if ( file ~ /\/Library\// ) {
# files to check manually
isKeep[file]
}
else if ( file ~ /\/(labs data|backup-current)\// ) {
# backup files to keep
isKeep[file]
backup = "yes"
}
else if ( match(file, /\/(backup-disk-name\/|backup-)([0-2][0-9][0-1][0-9][0-3][0-9])\//, pats) != 0 ) {
# files in historical backup directories
if ( pats[2] > latest ) {
latest = pats[2]
}
isDate[file] = pats[2]
}
else {
# unclassified files to check manually
isKeep[file]
}
}
for (file in isDate) {
if ( isDate[file] == latest && backup == "no") {
isKeep[file]
}
else {
isDelete[file]
}
}
for (file in isKeep) {
print "#", file
}
for (file in isDelete) {
# use single quotes to escape special characters in file
# use gensub() to escape single quotes in file
print "rm", "'" gensub(/'/,"'\\\\''", "g", file) "'"
}
delete files
}
最後に、いくつかの考えを共有したいと思います。あまり遠く離れていないといいですね。
数週間前、私はついにその巨大なバックアップをクリーンアップすることにしました(いくつかのファイルには10個以上の重複がありました)。ただし、このタスクを自動化するツールが見つかりません。私はこれのためにCプログラムを始めたくないし、Perlでやりたくない。だから私はシェルルートに行かなければならないことがわかりました。しかし、どこから始めるべきかわかりません。最初の行に閉じ込められました。
たくさん読んだ後もまだ混乱しています。それでSEに質問を投稿することにしました。
@Edのコードを初めて読んだとき、「一体何だ!」と思った。それからそれを受け取ったとき、それは効率的で明確な素晴らしいコードであることに気づきました。
だからここにあります。約1週間前、私はawk
RegExpについて何も知らず、ほとんど知りませんでした。これで、@Edの貢献のおかげで、私の最初のawk
スクリプトを書いて、RegExpの世界をよりよく理解し、面倒な作業を完了することができました。さらに重要なのは、RegExpawk
やその他のテキスト処理シェルツールを直接見ているほど自信があることです。これは私がSEにもっと貢献するように動機づけます。
私のように山に面するなど、困難な状況に直面している人たちに希望を与えようと私の個人的な経験を共有したかったのです。