同じ名前の子ディレクトリをディレクトリから検索し、子ディレクトリ内のすべてのファイルを親ディレクトリに移動する方法が必要です。したがって、 子ディレクトリは空のままにしておくことができます。 空のディレクトリをすべて削除する/recup-dir1/recup-dir1/files to /recup-dir1/files.
などの方法を使用できます。
find . -type -d -empty -delete
問題は、どのディレクトリに同じ名前の子ディレクトリがあり、どのディレクトリにないのかが分からないことです。
疑似コードでは次のようなものが必要です。
While more directories are unchecked
get name-x of next dir
enter dir
If name-x/name-x exist
move all files in name-x/name-x to name-x
mark dir as done
next
私の推測では、同じ名前の子を持つすべてのディレクトリのリストを作成し、このリストを次のようなコマンドでループする小さなPythonスクリプトを作成することです。
find something something -exec mv
おそらく、これは bash スクリプトで実行できるか、または別の解決策が存在します。rsync コマンドのようなものですが、おそらく rsync を使用してこの混乱を引き起こしたので、それが解決策になるとは思いません。
編集: 以下はツリー出力の実際の部分です: 最上位レベルのディレクトリは /mnt/external-disk/tst-backup 内にあります。下位レベルにはサブディレクトリはありません。
│ └── recup_dir.1
├── recup_dir.10
│ └── recup_dir.10
├── recup_dir.100
│ └── recup_dir.100
├── recup_dir.102
│ └── recup_dir.102
└── recup_dir.1020
└── recup_dir.1020
答え1
を使用するとzsh
、次の操作を実行できます。
for dir in **/*(NDodoN/e['[[ $REPLY:t = $REPLY:h:t ]]']); do
contents=($dir/*(NDoN))
(( $#contents == 0 )) ||
mv -- $contents $dir:h/ &&
rmdir -- $dir
done
どこ:
**/*(qualifiers)
glob 修飾子を使用した再帰的なグロブN
: nullglob: 一致しない場合は文句を言わないD
: dotglob: 隠しファイルも含めるod
: 深さを優先します (葉を枝より先に並べます)。oN
: それ以外の場合は、ファイル リストの順序を気にする必要はありません。/
: ディレクトリタイプのファイルに制限します。e['expression']
expression
:コードが true を返すファイル (現在のファイル パスが格納されているファイル$REPLY
)に制限します。$REPLY:t
: ファイルの末尾(ベース名)$REPLY:h:t
: ファイルの先頭 (dirname) の末尾
bash
4.4 以降と GNUfind
またはfind
ほとんどの BSD では、次のようにして同様の操作を実行できます。
shopt -s nullglob dotglob
readarray -td '' dirs < <(
LC_ALL=C find . -depth -regex '.*\(/[^/]*\)\1' -type d -print0
)
for d in "${dirs[@]}"; do
contents=("$d"/*)
(( ${#contents[@]} == 0 )) ||
mv -- "${contents[@]}" "${d%/*}/" &&
rmdir -- "$d"
done
今回は、./path/to/dir/dir
基本的な正規表現のバック参照を使用して、正規表現を使用してファイルを照合します。
答え2
GNU find
v4.8.0とBash v5.1.8をベースにしたこれを試してみてください
パート 1: ディレクトリ ツリーを解析し、サブディレクトリ名の重複を検出する
ツリー内の特定のディレクトリの構造が次のようになっていると仮定します。
./
|__test1/
|__dirname with space
| |__test2
| |__ test2
|__dirname **
| |__test1
|
|__reboot
| |__test1
|
|__test2/
|__test3/
|__test2/
|__test1/
|__test1/
(奇妙なディレクトリ名はコードの安全性を示すために存在します。)
いくつかのサブディレクトリ (サブディレクトリ) がさまざまな方法で繰り返されていることがわかります。 一部は 1 回だけでなく複数回繰り返され (例test1
)、1 つは繰り返されません ( test3
)。また、親と子として繰り返されるか、任意の数の中間サブディレクトリによって区切られる場合があります。
以下のコードは、ディレクトリ構造内のサブディレクトリ名の重複を詳細に明らかにします。
- ファイルツリーを解析してサブディレクトリ構造を見つけます。
$PWD
- ルート レベルを除いて、2 レベル以上のサブディレクトリ パスの各コンポーネントの重複を検出します
$PWD
。私の実験では、最長のサブディレクトリ パスは次のとおりです。./test1/test2/test1/test3/test2/test1/test1
- リーフから始めて、つまりサブディレクトリ パスを右から左に読み取り、各サブディレクトリ レベルで見つかった最初のサブディレクトリの重複を出力します。
- 印刷は逆の順序でファイルにリダイレクトされるため、最も長いサブディレクトリ パスが最初に表示されます。2 つの連続するセミコロンは、パス コンポーネント (「;;」の左側) と、前の箇条書きに従って見つかった最初の重複 (「;;」の右側) を区切ります。
[コード]
$ find ./* -type d -exec bash -c 'set -o noglob; IFS="/" subdir=($(printf "%s " "$1")); dirlevels=$((${#subdir[@]}-1)); dupe="$(awk '\''!($1 in sd) {sd[$1];next} {print $1}'\'' < <(printf "%s\n" ${subdir[@]:1}))";[ $dirlevels -ge 2 ] && [ ! -z "$dupe" ] && (printf "%s/" "${subdir[@]:1}";printf " ;; %s\n" "$(tail -n 1 < <(printf "%s\n" "$dupe"))";)' shellexec {} \; | tac >| tmp.data
$ cat -n tmp.data
1 test1/reboot/test1/ ;; test1
2 test1/dirname with space/test2/test2/ ;; test2
3 test1/test2/test1/test3/test2/test1/test1/ ;; test1
4 test1/test2/test1/test3/test2/test1/ ;; test1
5 test1/test2/test1/test3/test2/ ;; test2
6 test1/test2/test1/test3/ ;; test1
7 test1/test2/test1/ ;; test1
8 test1/dirname **/test1/ ;; test1
パート 2: サブディレクトリ名の重複の処理、コンテンツの移動
処理は に表示されている順序で行われますtmp.data
。
- の最初の行では
tmp.data
、パス上の最初の名前の重複./test1/test2/test1/test3/test2/test1/test1
は ですtest1
。その内容を同じ名前で左端のサブディレクトリ レベルに転送できます。./test1/
- 移動先の既存のファイルを上書きせずにコンテンツが移動されると、右端のサブディレクトリ レベル
test1
が削除されます。 - 2 行目に進み
tmp.data
、上記の手順を繰り返します。 - すべての行が
tmp.data
消費されるまで、など。
test1/*
この段階での質問は(質問の著者:@TomDerksさんへ) 、6行目の一番右をどうするかということです。全てその内容は、同じ名前の一番左のディレクトリ(この場合はパスの最初のサブディレクトリレベル)に移動されますか?「すべて」には、./test1/test2/test1/
同様にサブディレクトリtest3
とその内容は?
完全な解決策(パート 2)はこれにかかっています。