父子目錄同名,將檔案移到父目錄

父子目錄同名,將檔案移到父目錄

我需要一種方法來搜尋目錄中具有相同名稱的子目錄,然後將子目錄中的所有檔案移至父目錄。因此, /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 限定符進行遞歸 globbing
  • N: nullglob: 如果沒有匹配,不要抱怨
  • D: dotglob: 包含隱藏文件
  • od:順序深度優先(葉子在它們所在的分支之前)。
  • oN:否則不必費心排序文件清單。
  • /:限制為目錄類型的檔案。
  • e['expression']:限製expression程式碼傳回 true 的檔案(目前檔案路徑儲存在其中$REPLY)。
  • $REPLY:t: 文件的尾部(基本名稱)
  • $REPLY:h:t:檔案頭(​​目錄名)的尾部)

對於bash4.4+ 和 GNUfindfind大多數 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/

(奇怪的目錄名稱是為了證明程式碼安全性。)

您會看到一些子目錄(subdirs)以不同的方式重複。有些重複多次,而不僅僅是一次(例如test1),一個不重複(test3),它們可以作為父目錄和子目錄重複,也可以由任意數量的中間子目錄分隔。

下面的程式碼詳細地揭示了目錄結構中子目錄名稱的重複。

  • 它解析檔案樹以取得從以下位置開始的子目錄結構$PWD
  • 它會尋找 2 個或更多層級的任何子目錄路徑的每個元件的重複項,不計算根層級( )$PWD。在我的實驗中,最長的子目錄路徑是: ./test1/test2/test1/test3/test2/test1/test1
  • 它會列印在每個子目錄層級找到的第一個子目錄副本,從葉子開始,即從右到左讀取子目錄路徑。
  • 列印以相反的順序重定向到文件,因此首先顯示最長的子目錄路徑。兩個連續的分號將路徑組件(“;;”左側)與根據上一個項目符號找到的第一個重複項(“;;”右側)分開。

[代碼]

$ 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/test1test1。我們可以將其內容傳輸到最左邊的同名子目錄:./test1/
  • 一旦內容被移動且沒有破壞目標處的現有文件,最右邊的子目錄層級test1將被刪除。
  • 我們繼續執行第 2 行tmp.data並重複上述步驟。
  • 等等,直到所有行都tmp.data被消耗掉。

在這個階段,問題(問題的作者:@TomDerks)是如何處理test1/*第 6 行最右邊的內容?應該全部其內容是否被移到最左邊的同名目錄,在本例中是路徑上的第一個子目錄層級? 「全部」是否包含以下文件./test1/test2/test1/ 子目錄test3及其內容?
完整的解決方案(第 2 部分)取決於此。

相關內容