如何使用 find 命令删除目录而不删除某些子目录?

如何使用 find 命令删除目录而不删除某些子目录?

我正在尝试修改脚本中的以下命令,以根据年龄删除目录。

示例目录如下:

/a/b/c/2011/11/11
/a/b/c/2011/11/11/12-ACD-1234
/a/b/c/2011/11/14

当前不准确的尝试如下:

find /a/b -type d -mindepth 2 -depth ! -name '*ACD*' -mtime +180 -exec rm -rf {} +

虽然此脚本不会删除包含“ACD”的目录,但会删除父目录。我尝试过 -prune 组合和其他技巧,但无法获得所需的结果。

有没有办法完善此 find 命令以保留符合特定条件的子目录?在此示例中,必须保留父目录 /a/b/c/2011 和 /a/b/c/2011/11,以免递归删除子目录。

如果可能的话,我希望只使用 find 命令来执行此操作。

答案1

主要问题是 find 会为您提供一个漂亮的目录列表作为输出,但您需要一种方法来单独评估每个目录,以确定它们是否*ACD*在子目录中有位置。而且管道命令似乎不能很好地(或根本不能?)用作 find 的参数-exec

因此,一种方法是创建自己的-exec可以调用的命令脚本,如果其参数不包含子目录,则返回 0 *ACD*,否则返回 1。这似乎对我有用:

#!/bin/sh
# script: /path/to/hasnoACD
list=`find $1 -name '*ACD*'`
[ "x$list" = "x" ] && exit 0 || exit 1

然后您所需的查找命令只需使用-exec两次:

find /a/b -type d -mindepth 2 -depth ! -name '*ACD*' -mtime +180 -exec /path/to/hasnoACD {} \;  -exec rm -rf {} +

-exec /path/to/hasnoACD {} \;仅对您想要的情况才会评估为真。


另一种选择:我最初采用与 @icyrock-com 类似的方法,最终使用 find 命令通过管道传输到 while 循环,该循环处理目录删除。'while' 语法取决于您的 shell,您需要一种方法来区分包含某些子目录的目录,*ACD*就像我们上面使用脚本所做的那样hasnoACD,但在 FreeBSD 上的 bash 中,以下方法似乎有效:

find /a/b -type d -mindepth 2 -depth ! -name '*ACD*' -mtime +180 | while read mydir ; do find $mydir -name '*ACD*' | xargs false && rm -rf $mydir ; done

请注意 while 循环中的 find —— 仅当 find 未返回任何内容(无*ACD*子目录)时,xargs 才会返回 true,从而rm -rf $mydir执行。xargs 的这种行为在我的 FreeBSD 机器上有效,但在 Linux 或 Solaris 上,行为有所不同,因此需要进行其他测试。

正如@icyrock-com 所建议的,您可能希望执行 amv而不是 a rm,以确保安全。

答案2

不确定您是否可以find仅使用此功能,但您可以尝试这里有一个测试脚本 - 在 /tmp 或安全的地方尝试此脚本:

# Prepare
rm -rf a
mkdir -p a/b/c
for a in c d; do
  for i in 2010 2011; do
    for j in 05 11; do
      mkdir -p a/b/$a/$i/$j/will-delete
      if [ $j != 05 ]; then
        mkdir -p a/b/$a/$i/$j/will-ACD-leave
      fi
    done
  done
done
echo Created

# List
echo ----- Listing
find a

# Remove
echo ----- Removing
find a/b -depth -type d ! -name "*ACD*"|while read l; do
  num_entries=`ls -a "$l"|wc -l`
  if [ $num_entries -eq 2 ]; then
    echo Removing $l
    rm -rf "$l"
  fi
done

# List
echo ----- Listing
find a

请注意,以上删除了-mtime +180测试,因此您需要根据需要进行调整。输出将是这样的:

Created
----- Listing
a
a/b
a/b/c
a/b/c/2010
a/b/c/2010/05
a/b/c/2010/05/will-delete
a/b/c/2010/11
a/b/c/2010/11/will-delete
a/b/c/2010/11/will-ACD-leave
a/b/c/2011
a/b/c/2011/05
a/b/c/2011/05/will-delete
a/b/c/2011/11
a/b/c/2011/11/will-delete
a/b/c/2011/11/will-ACD-leave
a/b/d
a/b/d/2010
a/b/d/2010/05
a/b/d/2010/05/will-delete
a/b/d/2010/11
a/b/d/2010/11/will-delete
a/b/d/2010/11/will-ACD-leave
a/b/d/2011
a/b/d/2011/05
a/b/d/2011/05/will-delete
a/b/d/2011/11
a/b/d/2011/11/will-delete
a/b/d/2011/11/will-ACD-leave
----- Removing
Removing a/b/c/2010/05/will-delete
Removing a/b/c/2010/05
Removing a/b/c/2010/11/will-delete
Removing a/b/c/2011/05/will-delete
Removing a/b/c/2011/05
Removing a/b/c/2011/11/will-delete
Removing a/b/d/2010/05/will-delete
Removing a/b/d/2010/05
Removing a/b/d/2010/11/will-delete
Removing a/b/d/2011/05/will-delete
Removing a/b/d/2011/05
Removing a/b/d/2011/11/will-delete
----- Listing
a
a/b
a/b/c
a/b/c/2010
a/b/c/2010/11
a/b/c/2010/11/will-ACD-leave
a/b/c/2011
a/b/c/2011/11
a/b/c/2011/11/will-ACD-leave
a/b/d
a/b/d/2010
a/b/d/2010/11
a/b/d/2010/11/will-ACD-leave
a/b/d/2011
a/b/d/2011/11
a/b/d/2011/11/will-ACD-leave

上述操作将删除空的父文件夹,因此,如果这是问题所在,您可能需要修改循环while以在每个文件夹后创建一个虚拟文件rm,然后执行另一个顶层操作find来删除这些文件(以某种方式命名它们以便于查找)。

另外需要注意的是,我建议不要删除,mv而是在某处执行,直到您确定这是您想要的。之前备份是另一种选择。

希望这可以帮助。

相关内容