Доступна ли обобщенная функция 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 ответа
Мне удалось придумать более продвинутую версию моих собственных начальных функций:
только файлы 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
команду заново.- Мой код не проверяется