我正在尝试对许多包含文件的目录递归地执行一系列操作。在每个目录中,我想创建 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/
*/
root
mkdir
-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
时找到的,因此它将停留在那里并执行从那里指定的命令。当然,里面没有文件,因此不会移动任何内容,但还要注意,不会产生错误,如find
root
mkdir
root
mkdir -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_b
subdirectory1
subdirectory2
a.tiff
subdirectory1
b.tiff
subdirectory_b
/dev/null
-p
mkdir
而不是您拥有的内容,请从内部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/files
formkdir
和mv
(由 调用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
自身的文件)。