使用 Bash,如何找到所有具有两个目录的目录并将它们移动到复制的树结构中?

使用 Bash,如何找到所有具有两个目录的目录并将它们移动到复制的树结构中?

假设我有以下目录树结构:

./Original
  ├── Dir1
  │   ├── Objects
  │   └── Textures
  ├── Dir2
  │   ├── SubDir1
  │   │   └── SubSubDir1
  │   │       ├── Objects
  │   │       └── Textures
  │   └── SubDir2
  │       ├── Objects
  │       └── Textures
  └── Dir3
      ├── Objects
      ├── SubDir1
      │   ├── Objects
      │   └── Textures
      └── Textures

而且我也有一个类似的结构(它实际上是原始目录的副本,没有对象和纹理目录:

./Copy
  ├── Dir1
  ├── Dir2
  │   ├── SubDir1
  │   │   └── SubSubDir1
  │   └── SubDir2
  └── Dir3
      └── SubDir1

如何将所有对象和纹理目录及其文件从原始目录移动到复制目录中的相应位置?完成后应如下所示:

./Original
  ├── Dir1
  ├── Dir2
  │   ├── SubDir1
  │   │   └── SubSubDir1
  │   └── SubDir2
  └── Dir3
      └── SubDir1

./Copy
  ├── Dir1
  │   ├── Objects
  │   └── Textures
  ├── Dir2
  │   ├── SubDir1
  │   │   └── SubSubDir1
  │   │       ├── Objects
  │   │       └── Textures
  │   └── SubDir2
  │       ├── Objects
  │       └── Textures
  └── Dir3
      ├── Objects
      ├── SubDir1
      │   ├── Objects
      │   └── Textures
      └── Textures

注意事项:

  • 原始目录中并非所有目录都有对象和纹理目录
  • 对象和纹理目录被移动而不是复制
  • 此外,原始目录和复制目录位于同一目录中,这是应该调用命令/函数的地方

请使用此命令来构建起始结构:

mkdir -p Original/Dir1/Objects Original/Dir1/Textures Original/Dir2/SubDir1/SubSubDir1/Objects Original/Dir2/SubDir1/SubSubDir1/Textures Original/Dir2/SubDir2/Objects Original/Dir2/SubDir2/Textures Original/Dir3/Objects Original/Dir3/Textures Original/Dir3/SubDir1/Objects Original/Dir3/SubDir1/Textures
mkdir -p Copy/Dir1 Copy/Dir2/SubDir1/SubSubDir1 Copy/Dir2/SubDir2 Copy/Dir3/SubDir1

答案1

这是一个模拟。运行后,如果确定一切正确,请删除echo并再次运行:

shopt -s globstar
for d in **/{Objects,Textures}; do
    echo mv "$d" "Copy/${d##Original/}"
done

解释:

  • 打开 Bash 选项“globstar”会使 glob**/匹配零个或多个子目录。
  • Bash 扩展**/{Objects,Textures}**/Objects **/Textures
  • ${d##Original/}Original/意思是“从 的开头删除字符串$d

答案2

如果你想使用另一个位置来更新目录树,最好的工具是rsync,次好的可能是tar。编写 shell 脚本往往是小菜一碟:

rsync -aP --ignore-existing Original/ Copy

选项:

  • -a告诉rsync保留所有权、模式等。
  • -P打印进度信息
  • --ignore-existing意味着已经存在的文件Copy将不会被触及。
  • 尾随的后面/指示Originalrsync的内容复制OriginalCopy,而不是复制Original自身。

Original完成后删除,或使用--remove-source-files文件选项rsync在复制完成时删除文件。

答案3

这是你可以设法搬家的一种方法没有子目录的目录

首先开启递归遍历:

shopt -s globstar

现在,你可以使用 进行递归遍历**。你可以使用 将其关闭shopt -u globstar,但下次打开 shell 时它仍然会关闭。

Copy现在在和的父目录中Original

for d in Original/**/; do $(ls -lAq "$d" | grep -q '^d') || echo mv -vn -- "$d" Copy"${d/Original/}"; done

测试后删除echo以实际移动目录(当然还有它们的内容 -mv没有-r标志)

让我们让它更易读一些

shopt -s globstar
for d in Original/**/; do 
    $(ls -lAq "$d" | grep -q '^d') || 
    echo mv -vn -- "$d" Copy"${d/Original/}"
done

等等,我刚才是不是解析输出ls?嗯,是的,我这样做了。但我认为这没问题(!)因为我认为在这种情况下我可以依赖输出的一致性。无论文件名包含什么字符,该ls -l文件所在目录的输出都将以定义其类型和权限的字符串开头。在ls -l所有目录列表中,都以 开头d,例如

drwxrwxr-x 26 zanna zanna 4096 Oct  5 19:57 playground

即使换行符也不会搞乱这一点,因为?如果我们添加,它们会显示为(即使在管道之后)-q

$(ls -lAq "$d" | grep -q '^d') 

检查目录是否有一些子目录。grep我们收集退出状态,而不是收集 的输出。如果命令成功,我们什么也不做,但如果失败,我们收集mv目录(使用||或 运算符,这意味着,如果前面的命令||失败,则执行后面的命令)。

我们使用字符串操作来删除Original,以便将目录复制到正确的位置Copy

结果如下:

$ tree
.
├── Copy
│   ├── Dir1
│   │   ├── Objects
│   │   └── Textures
│   ├── Dir2
│   │   ├── SubDir1
│   │   │   └── SubSubDir1
│   │   │       ├── Objects
│   │   │       └── Textures
│   │   └── SubDir2
│   │       ├── Objects
│   │       └── Textures
│   └── Dir3
│       ├── Objects
│       ├── SubDir1
│       │   ├── Objects
│       │   └── Textures
│       └── Textures
└── Original
    ├── Dir1
    ├── Dir2
    │   ├── SubDir1
    │   │   └── SubSubDir1
    │   └── SubDir2
    └── Dir3
        └── SubDir1

答案4

以下是此方法可能对您起作用的示例。请注意,此脚本将与原始目录和复制目录在同一级别运行,并且可能需要根据您的实际需求进行调整。祝您好运。

#!/bin/bash

src="original" 
dst="./copy"

function findLocation {
    for i in `find $src/ -type d -name "$1"`; do
        IFS='/' read -ra directories <<< "$i"
        dirNum=${#directories[@]}
        let "arrayNum=$dirNum-2"

        arrs=()

        for ((k=1 ; k<=$arrayNum; k++)); do
            arrs+=("${directories[$k]}")
        done

        echo "===============================++++++++++++++++"

        function join {
            local IFS="$1"
            shift
            echo "$*"
        }

        constructDst=`join / ${arrs[@]}`
        destination="$dst/$constructDst/"

        if [ ! -d $destination ] ; then
            mkdir -p $destination
            echo "creating directory: $destination"
        fi

        if [ -d $i ]; then
            echo "Moving $i to $destination"
            mv $i -t $destination
        fi
    done
}

findLocation Textures 
findLocation Objects

相关内容