親子ディレクトリが同じ名前の場合、ファイルを親ディレクトリに移動します

親子ディレクトリが同じ名前の場合、ファイルを親ディレクトリに移動します

同じ名前の子ディレクトリをディレクトリから検索し、子ディレクトリ内のすべてのファイルを親ディレクトリに移動する方法が必要です。したがって、 子ディレクトリは空のままにしておくことができます。 空のディレクトリをすべて削除する/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) の末尾

bash4.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 findv4.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)はこれにかかっています。

関連情報