父子目录同名,将文件移动到父目录

父子目录同名,将文件移动到父目录

我需要一种方法来搜索目录中具有相同名称的子目录,然后将子目录中的所有文件移动到父目录。因此, /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 部分)取决于此。

相关内容