是否存在一个通用的 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
。
答案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 points
。find
我们不想真正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
我已经设法想出了我自己的初始功能的更高级版本:
仅限文件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.
仍然好奇是否有其他人有其他解决方案。