通用 chmod 函数区分目录和文件(即使用 find)

通用 chmod 函数区分目录和文件(即使用 find)

是否存在一个通用的 bash 函数可以chmod在各个方面进行模拟,此外它还可以让我区分文件和目录?

我知道已经有很多例子,比如下面这个这个答案

find /path/to/base/dir -type d -exec chmod 755 {} +
chmod 755 $(find /path/to/base/dir -type d)
find /path/to/base/dir -type d -print0 | xargs -0 chmod 755 

...但这些都是硬编码的参数,记忆和输入都很繁琐。1 )

我想将它概括化并使其动态化,以便我能够做如下的事情,例如:

# for directories
chmod -d <any valid argument list for chmod> # or
chmodd <any valid argument list for chmod>


# for files
chmod -f <any valid argument list for chmod> # or
chmodf <any valid argument list for chmod>

我曾尝试自己创建一个可行的解决方案,但是由于我的 bash 技能低于标准,而且我不确定如何解析正确的参数并将它们插入到正确的位置,所以它非常粗糙且有限:

function chmodf {
  find . -mindepth 1 -type f -print0 | xargs -0 chmod "$@"
}

function chmodd {
  find . -mindepth 1 -type d -print0 | xargs -0 chmod "$@"
}

当然,我更喜欢的是下面这样的东西(伪代码):

function chmodd {
  paths = extract paths from arguments list
  recursive = extract recursive option from arguments list
  otherargs = remaining arguments
  if( recursive ) {
    find <paths> -mindepth 1 -type d -print0 | xargs -0 chmod <otherargs>
  }
  else {
    find <paths> -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 chmod <otherargs>
  }
}

您是否已经知道这种函数/二进制文件的存在,或者您能帮助我实现这个目标吗?


我想要这个的主要原因是,我发现自己经常需要递归设置目录上的 setgid 位,而不是文件上的。但据我所知,g+Schmod 没有大写字母选项。


1) 我知道 Unix 的格言是“做一件事,做好它”,或者类似的东西,但说实话,我无法理解,至少据我所知,在这种行为存在了近半个世纪chmod并看到无数次要求之后,为什么chmod还没有用这个功能进行修改。这似乎是一个显而易见且合适的功能chmod

答案1

我的尝试:

chmodx() (
   type="$1"
   shift
   cleared=
   marked=
   recursive=
   for a do
      if [ -z "$cleared" ]; then
         set -- -type "$type" -exec chmod
         cleared=y
      fi
      if [ -z "$marked" ]; then
         case "$a" in
         -- )
            set -- "$@" -- {} +
            if [ -z "$recursive" ]; then
               set -- -prune "$@"
            fi
            marked=y
            ;;
         -R|--recursive )
            recursive=y
            ;;
         * )
            set -- "$@" "$a"
            ;;
         esac
      else
         set -- "$a" "$@"
      fi
   done
   if [ -z "$marked" ]; then
      printf '`--'"'"' required\n'
      return 1
   fi
   exec find "$@"
)

alias chmodf='chmodx f'
alias chmodd='chmodx d'

函数和别名依次

chmodf  args for chmod  --  starting points

进入

find  points starting  -type f -exec chmod  args for chmod  {} +

同样chmodd …变成find … -type d …

它主要涉及重新排列参数。几乎没有解析。之前的所有内容--都被视为args for chmod。之后的所有内容--都被视为starting pointsfind我们不想真正chmod递归运行,但我们可能会或可能不会find递归运行。非递归操作是通过-prune在正确位置执行的。如果有-R或,--recursive那么args for chmod它将消失但-prune不会出现。

笔记:

  • 我认为代码是可移植的。我故意避免使用数组。

  • chmodf并且chmodd可以是函数。即使这样,它们也应该调用chmodx以保留代码干燥将它们作为两个独立的函数来实现并不优雅。

  • --是强制性的因为我决定。当然可以区分路径名和其他参数,而无需使用 将它们分开--。实际上chmod就是这样。它知道自己的语法、所有可能的选项等等;所以其他一切都必须是要操作的文件。将这些知识和功能嵌入到 shell 脚本中是湿的。如果您确实希望事情正常进行,--那么您应该通过改进chmod、修改其源代码来解决问题。

  • 滥用的方式有:

    • 我的代码没有验证starting points,所以您可以通过-maxdepth 3这种方式注入例如(3 -maxdepth因为starting points成为points starting)。
    • 类似地,没有对 的验证args for chmod。如果args for chmod包含;或 ,{} +-exec主命令将在行的早期终止。这允许您注入更多主命令;或者通过生成无效语法来破坏整个命令。

    虽然技术上可行,但这些技巧很丑陋。如果我需要让事情复杂化,我绝对宁愿find重新编写命令。

答案2

我已经设法想出了我自己的初始功能的更高级版本:

仅限文件chmodchmodf):

function chmodf {
  local maxdepth='-maxdepth 1'
  local paths=()
  local args=();
  for arg do
    if [[ "$arg" == '-R' ]]; then
      maxdepth=''
    elif [[ -e "$arg" ]]; then
      paths+=("$arg")
    else 
      args+=("$arg")
    fi
  done
  if [ -z "$paths" ]; then
    paths+=('.')
  fi
  set -- ${args[@]}
  find "${paths[@]}" $maxdepth -type f -print0 | xargs -0 chmod "$@"
}

仅限目录chmodchmodd):

function chmodd {
  local maxdepth='-maxdepth 0'
  local paths=()
  local args=();
  for arg do
    if [[ "$arg" == '-R' ]]; then
      maxdepth=''
    elif [[ -e "$arg" ]]; then
      paths+=("$arg")
    else 
      args+=("$arg")
    fi
  done
  if [ -z "$paths" ]; then
    paths+=('.')
  fi
  set -- ${args[@]}
  find "${paths[@]}" $maxdepth -type d -print0 | xargs -0 chmod "$@"
}

我唯一担心的是,某些参数可能含糊不清,并被错误地解释为现有文件if [[ -e "$arg" ]]; then。不确定通常是如何处理的,即使有getopts。但除此之外,它似乎按我的预期工作。

例子:

chmodf g+rx ./* # add group read and execute bit to first level files in ./*
chmodf -R g+rx ./* # add group read and execute bit recursively to files in ./*

chmodd g+rx ./* # add group read and execute bit to first level directories in ./*
chmodd -R g+rx ./* # add group read and execute bit recursively to directories in ./*
# etc.

仍然好奇是否有其他人有其他解决方案。

相关内容