如何删除没有写权限的目录中的硬链接?

如何删除没有写权限的目录中的硬链接?

我正在根据此配方配置快照每日备份:http://www.mikerubel.org/computers/rsync_snapshots/

总而言之,在备份服务器上,我通过运行以下命令轮换旧副本:

dir=backup_directory
rm -rf ${dir}.3            # <-- this command reports "Permission denied" errors
mv     ${dir}.2 ${dir}.3
mv     ${dir}.1 ${dir}.2
cp -al ${dir}   ${dir}.1

在本地服务器上:

rsync --del -rvatlz local_directory_to_backup/ backup_server:backup_directory/. --exclude=".*" --ignore-errors

由于rsync保留权限,出现部分备份目录没有写权限,导致rm -rf删除失败。

我知道我可以chmod -R +w ${dir}.3先运行,rm但我想知道是否有更简单的方法?我没有sudo备份服务器的访问权限或 root 帐户。

答案1

警告

  1. 这个答案中的代码可能比更具破坏性rm -rf我尝试编写好的代码,但错误还是会发生。忘记我的名声吧,我只是互联网上的一个普通人。假设代码会删除所有你可以删除的文件。减轻风险的几种方法:

    • 在虚拟机中测试代码;
    • 创建一个只会损害自己利益的新用户帐户;
    • 将重要文件备份到外部硬盘,验证,然后物理断开硬盘;
    • 理解代码,然后才决定是否要使用它(棘手的)。
  2. 在你使用的问题中rm -rf ${dir}.3。在许多 shell 中你应该双引号。如果保留${dir}不引号,您可能会删除超出您想要的内容。


建造核弹

(你好,NSA。我就知道你会来的。)

chmod -R之前rm非常简单。您可以通过将任何复杂性隐藏在 shell 函数或脚本后面来进一步简化它。开发一次,然后在需要时只需调用其名称即可。脚本可以无条件地chmod在之前运行rm;或者它可以运行rm并希望成功,如果第一个失败则尝试chmod+ 。rmrm

我知道你不想chmod影响所有人文件因为其中一些文件可能也链接到您要删除的目录之外。换句话说:可能存在硬链接。文件的模式位(权限)存储在其 inode 中,而不是指向 inode 的目录条目中;因此,无法chmod独立编辑指向同一 inode 的多个条目(路径名)。如果您不想删除chmod将在其他目录中保留的文件,那么您就不应该chmod -R盲目地这么做。您的担心是有道理的。

出现问题时,您需要的总是目录chmod。Linux 不允许硬链接到目录,因此chmod删除有问题的目录不会产生任何其他影响(而且该目录无论如何都会被删除)。如果您只能chmod -R删除目录,那么“ chmod+ rm”解决方案就很好了。

请参阅这个问题:chmod区分目录和文件的通用函数。 在我的答案在那里您将找到一个名为的 shell 函数,chmodx专门用于处理chmod某些类型的文件。让我们使用它来解决您的问题:

nuke() {
  [ "$#" = 0 ] && return 0
  chmodx d -R u+w -- "$@"
  rm -rf -- "$@"
}

用法:nuke foo ./bar /baz/qux

笔记:

  • --rm 将使其停止解析选项,但--afterchmodx不会以这种方式工作。如果您运行nuke -whateverthen find(inside chmodx) 会出现错误。因此您应该nuke ./-whatever改为这样做。下面我将展示如何解决这个问题。

  • rm -rf --没有其他参数则不执行任何操作。chmodx d -R u+w --没有其他参数则find没有任何起始点。在这种情况下find使用的一些实现.。with 行[ "$#" = 0 ]使得nuke没有参数的调用成为无操作,即使chmodx d -R u+w --不会是无操作。

  • chmodxchmod针对每个目录运行。nuke您可以修改其代码,并将-perm(参见)插入到需要删除的目录man 1 find中的正确位置。或者您可以像这样滥用注入:chmodchmodchmodx! -perm -u+w

    nuke() {
      [ "$#" = 0 ] && return 0
      chmodx d -R u+w -- -u+w -perm ! "$@"
      rm -rf -- "$@"
    }
    

    此变体仅检查并设置用户的权限。这种方法似乎适合您的使用情况。

处理目录树(或树)chmodx然后用处理相同的树rm似乎不是最佳选择。如果您的find支持-delete可能不会) 则find foo -delete不应比 慢很多rm -rf foo,除非dir1/dir2/…/dirN/fileZ无法删除但仍find试图删除dirN的情况dir1。注意( 的一些实现)rm可能知道尝试删除这些目录是没有意义的。

find foo -delete可以改进,如果-delete失败了可以采取一些措施。-execdir(您find可能支持也可能不支持)解决您的问题的基本方法可以是:

nuke() {
   find "$@" ! -delete -execdir chmod u+w . \; -delete
}

主要优点是目录树不会被处理两次。chmod只有在需要时才会生成附加进程 ( )。缺点如下:

  1. 如果第一个-delete失败,则会将一条消息打印到 stderr。您可以使用2>/dev/null但其他(可能有用的)消息也会被抑制。我们可以使用rm -f … 2>/dev/null而不是-delete这需要一个 shell. shell 和rm是我们想要避免的额外进程。
  2. nuke -whatever会造成混淆find。使用nuke ./-whatever
  3. (上面)的例子dir1/dir2/…/dirN/fileZ仍然适用。
  4. (优势或劣势)nuke /foo/bar最终会尝试删除bar。如果尝试失败,则将chmod u+w处理foo不是意味着要删除。您可能想要或不想更改 的模式位foo。如果您不介意更改它们,那么chmod u+w比 好得多chmod +w。请注意,在相同情况下, 的变体chmodx将保持foo不变,因此它可能不会删除bar

我认为你不能轻易地建立一个find命令,chmod只在需要chmod删除目录和-delete其中的文件时才会删除目录之后。这是因为-delete暗示-depth-depth意味着目录的内容将在目录本身之前被处理。

好的,让我们构建改进的nuke

nuke() (
   [ "$#" = 0 ] && exit 0
   err=0
   for p do
      case "$p" in
      -* )
         p="./$p"
      ;;
      esac
      2>/dev/null find "$p" ! -delete -execdir chmod u+w . \; -delete
      if [ -e "$p" ]; then
         >&2 printf '%s survived\n' "$p"
         err=1
      fi
   done
   exit "$err"
)

该函数逐个删除其参数。它将幸存者报告给 stderr。-支持以 开头的路径。如果需要,nuke /foo/bar请注释。chmod … /foo

该函数使用子shell有两个原因:

  • 使所有变量都变为局部变量;
  • 允许exit而不是return,这样您就可以将函数主体逐字粘贴到文件中并根据需要制作独立脚本(唯一需要添加的是shebang)。

shell 代码是可移植的(应该可以在 中工作sh)。不可移植的是-delete-execdir

相关内容