Обобщенная функция 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+S вариант для chmod.


1) Я знаю адажио unix «Делай одно и делай это хорошо» или что-то в этом роде, но, честно говоря, я не могу понять, как, по крайней мере, насколько мне известно, после почти полувека Существование chmod и увидев многочисленные запросы на такое поведение, chmod эта функция еще не была изменена. Вроде такой очевидный и подходящий функционал для chmod иметь.

2 ответа
2

Мне удалось придумать более продвинутую версию моих собственных начальных функций:

только файлы chmod (chmodf):

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 "$@"
}

только каталоги chmod (chmodd):

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.

Все еще любопытно, есть ли у кого-нибудь еще альтернативные решения.

    Моя попытка:

    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 '`--'"'"' requiredn'
          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 points за find. Если там есть -R или же --recursive в args for chmod тогда он исчезнет и -prune вместо этого появится в нужном месте.

    Примечания:

    • Я думаю, что код переносим. Я сознательно избегал использования массивов.

    • chmodf и chmodd могут быть функции. Даже тогда они должны позвонить chmodx сохранить код СУХОЙ. Реализация их как двух независимых функций неуместна.

    • -- является обязательным потому что я решил ЦЕЛОВАТЬ. Конечно, можно отличить пути от других аргументов, не разделяя их --. Фактически chmod Является ли это. Он знает свой собственный синтаксис, все возможные варианты и тому подобное; так что все остальное должно быть файлами, с которыми нужно работать. Встраивание этих знаний и функций в сценарий оболочки — это СМАЧИВАТЬ. Если вы действительно хотите, чтобы все работало без -- тогда вам лучше решить свою проблему, улучшив chmod, изменяя его источник.

    • Есть способы злоупотребления:

      • Мой код не проверяется starting points, поэтому вы можете ввести, например, -maxdepth 3 таким образом (как 3 -maxdepth потому что starting points становиться points starting).
      • Точно так же нет подтверждения args for chmod. Если args for chmod включают ; или же {} + затем -exec первичный будет завершен в начале линии. Это позволяет вводить больше первичных цветов; или сломать всю команду, сгенерировав неверный синтаксис.

      Хотя это технически возможно, такие уловки уродливы. Если бы мне нужно было что-то усложнять, я бы определенно предпочел написать find команду заново.

      Добавить комментарий

      Ваш адрес email не будет опубликован. Обязательные поля помечены *