递归文件和目录操作

递归文件和目录操作

我正在尝试对许多包含文件的目录递归地执行一系列操作。在每个目录中,我想创建 2 个目录并将一些文件移动到每个目录中。

例如,我的目录结构如下所示......

   root --
     subdirectory1 --
         file_a1_a.tif
         file_a2_a.tif
         file_a1_b.tif
         file_a2_b.tif
     subdirectory2 --
         file_b1_a.tif
         file_b2_a.tif
         file_b1_b.tif
         file_b2_b.tif

我想将文件重新组织为这样......

   root --
     subdirectory1 --
         subdirectory_a --
             file_a1_a.tif
             file_a2_a.tif
         subdirectory_b --
             file_a1_b.tif
             file_a2_b.tif
     subdirectory2 --
         subdirectory_a --
             file_b1_a.tif
             file_b2_a.tif
         subdirectory_b --
             file_b1_b.tif
             file_b2_b.tif

我正在执行以下操作,但它似乎只在顶级目录中工作。

find . -type d -execdir bash -c "mkdir -p subdirectory_a && mkdir -p subdirectory_b && mv *_a.tif subdirectory_a 2>/dev/null && mv *_b.tif subdirectory_b 2>/dev/null" \;

任何帮助将不胜感激

答案1

由于root只有孩子而没有孙子,您可以从root目录运行它:

subA='subdirectory_a'
subB='subdirectory_b'
for d in */; do
    mkdir -p "$d$subA" "$d$subB"
    mv "$d"*_a.tif "$d$subA"
    mv "$d"*_b.tif "$d$subB"
done

$d由于 for 循环上的 glob ,扩展为subdirectory1/和,它仅匹配直接子目录。创建相应的孙子文件(存在只是为了如果它们已经存在就不会抱怨),然后再次使用通配符将文件移动到孙子文件。subdirectory2/*/rootmkdir-p

注意,如果 shell 要扩展 glob,则应取消引用。这就是为什么mv "$d"*_a.tif "$d$subA"是正确的,但mv "$d*_a.tif" "$d$subA"不正确。

答案2

您在问题中发布的命令

find . -type d -execdir bash -c "mkdir -p subdirectory_a && mkdir -p subdirectory_b && mv *_a.tif subdirectory_a 2>/dev/null && mv *_b.tif subdirectory_b 2>/dev/null" \;

行为正确(也就是说,它做了它应该做的事情)。但它所表达的并不是你想要的。

该参数的运行方式是“...指定的命令从 包含匹配文件的子目录-execdir运行”(源代码,
查找(1)

也就是说,当你当前的工作直接是root并且命令定位到它时subdirectory1,它不会按照你的预期下降到那里。subdirectory1匹配项是在inside-type d时找到的,因此它将停留在那里并执行从那里指定的命令。当然,里面没有文件,因此不会移动任何内容,但还要注意,不会产生错误,如findrootmkdirrootmkdir -p目录(1):“如果存在则没有错误”。


那么我们如何让这场比赛成为你想要的呢?让我们假设您发布的结构相同

$ tree /tmp/root
/tmp/root
├── subdir1
│   ├── file_a1_a.tif
│   ├── file_a1_b.tif
│   ├── file_a2_a.tif
│   └── file_a2_b.tif
└── subdir2
    ├── file_b1_a.tif
    ├── file_b1_b.tif
    ├── file_b2_a.tif
    └── file_b2_b.tif

我建议将其分为三步。无需使用一个命令来完成所有工作。专注于期望的结果而不是“代码高尔夫”。所以先创建目录

$ find /tmp/root -maxdepth 1 -mindepth 1 -exec bash -c 'mkdir ${1}/subdirectory_{a,b}' sh  {} \;
$ tree
.
├── subdir1
│   ├── file_a1_a.tif
│   ├── file_a1_b.tif
│   ├── file_a2_a.tif
│   ├── file_a2_b.tif
│   ├── subdirectory_a
│   └── subdirectory_b
└── subdir2
    ├── file_b1_a.tif
    ├── file_b1_b.tif
    ├── file_b2_a.tif
    ├── file_b2_b.tif
    ├── subdirectory_a
    └── subdirectory_b

6 directories, 8 files

然后在目录树中找到特定级别的常规文件并移动它们。现在我们可以用它-execdir来实现我们想要的

$ find /tmp/root -maxdepth 2 -mindepth 2 -type f -execdir bash -c 'case ${1} in *a.tif) echo cp ${1} ./subdirectory_a ;; *b.tif) echo cp ${1} subdirectory_b ;; esac' sh {} \;
cp ./file_b2_b.tif subdirectory_b
cp ./file_b2_a.tif ./subdirectory_a
cp ./file_b1_a.tif ./subdirectory_a
cp ./file_b1_b.tif subdirectory_b
cp ./file_a1_b.tif subdirectory_b
cp ./file_a1_a.tif ./subdirectory_a
cp ./file_a2_b.tif subdirectory_b
cp ./file_a2_a.tif ./subdirectory_a

请注意,在这种情况下,我在移动之前使用 echo 以确保一切正常运行,并且不进行实际移动。因此,如果我们删除echo,这将表现正确:

$ find /tmp/root -maxdepth 2 -mindepth 2 -type f -execdir bash -c 'case ${1} in *a.tif) cp ${1} ./subdirectory_a ;; *b.tif) cp ${1} subdirectory_b ;; esac' sh {} \;
$ tree /tmp/root
/tmp/root
├── subdir1
│   ├── file_a1_a.tif
│   ├── file_a1_b.tif
│   ├── file_a2_a.tif
│   ├── file_a2_b.tif
│   ├── subdirectory_a
│   │   ├── file_a1_a.tif
│   │   └── file_a2_a.tif
│   └── subdirectory_b
│       ├── file_a1_b.tif
│       └── file_a2_b.tif
└── subdir2
    ├── file_b1_a.tif
    ├── file_b1_b.tif
    ├── file_b2_a.tif
    ├── file_b2_b.tif
    ├── subdirectory_a
    │   ├── file_b1_a.tif
    │   └── file_b2_a.tif
    └── subdirectory_b
        ├── file_b1_b.tif
        └── file_b2_b.tif

6 directories, 16 files

当然请注意,这使用复制来避免数据丢失。我们可以进一步清理,最后 - 我们得到所需的目录树结构。

$ find /tmp/root -maxdepth 2 -mindepth 2 -type f -delete
$ tree 
.
├── subdir1
│   ├── subdirectory_a
│   │   ├── file_a1_a.tif
│   │   └── file_a2_a.tif
│   └── subdirectory_b
│       ├── file_a1_b.tif
│       └── file_a2_b.tif
└── subdir2
    ├── subdirectory_a
    │   ├── file_b1_a.tif
    │   └── file_b2_a.tif
    └── subdirectory_b
        ├── file_b1_b.tif
        └── file_b2_b.tif

6 directories, 8 files

总之,我们所做的事情可以表达为一个脚本

find /tmp/root -maxdepth 1 -mindepth 1 -exec bash -c 'mkdir ${1}/subdirectory_{a,b}' sh  {} \; &&
find /tmp/root -maxdepth 2 -mindepth 2 -type f -execdir bash -c 'case ${1} in *a.tif) cp ${1} ./subdirectory_a ;; *b.tif) cp ${1} subdirectory_b ;; esac' sh {} \; &&
find /tmp/root -maxdepth 2 -mindepth 2 -type f -delete

当然,如果我们使用mv代替 ,则步骤 2 和步骤 3 可以压缩为一个cp,但我宁愿谨慎行事,也不愿冒数据丢失的风险。

答案3

在目录内运行时root,您的命令将在顶层创建subdirectoy_a和,以及和,然后将所有以 结尾的文件移动到和所有以 结尾的文件移动到,这不是您想要的。如果您没有将错误重定向到并且实际上可以看到它们,这将使您更容易排除故障。您也不需要选项,因为父目录已经存在。subdirectory_bsubdirectory1subdirectory2a.tiffsubdirectory1b.tiffsubdirectory_b/dev/null-pmkdir

而不是您拥有的内容,请从内部root(或实际调用的目录)运行它。

mkdir subdirectory1/subdirectory_a subdirectory1/subdirectory_b subdirectory2/subdirectory_a subdirectory2/subdirectory_b && mv subdirectory1/file_a*a*  subdirectory1/subdirectory_a && mv subdirectory1/file_a*b*  subdirectory1/subdirectory_b && mv subdirectory2/file_b*a*  subdirectory2/subdirectory_a && mv subdirectory2/file_b*b*  subdirectory2/subdirectory_b

答案4

zsh

cd root || exit
subdirs=(*(-/))
mkdir -p -- $^subdirs/subdirectory_{a,b} || exit
autoload -Uz zmv
zmv '(*/)*_(a|b).tif' '${1}subdirectory_$2/$f:t'

如果需要的话递归的subdir1/subsubdir1正如您的问题所说(但您的示例没有建议),也就是说,如果您还需要在等等中完成此操作,请将*s*(-/)(*/)上面的 替换为**

如果有无数的文件并且速度是一个问题,请运行zmodload zsh/filesformkdirmv(由 调用zmv)以成为 zsh 内置命令。

为一个试运行哪里zmv只告诉你它是什么做,添加一个-n选项。

如果您只想在分别包含和的目录中创建subdirectory_a和,则可以采用其他方法:subdirectory_b*_a.tif*_b.tif

mkmv() { mkdir -p -- $2:h && mv -- "$@"; }
zmv -P mkmv '(**/)*_(a|b).tif' '${1}subdirectory_$2/$f:t'

(该文件也会移动tif位于其root自身的文件)。

相关内容