我很久以前制作了这个剧本(非常快...... :))并经常使用它,但现在我对bash专家如何优化它感兴趣。
它的作用是通过当前目录中的文件和目录并设置正确的权限:
#!/bin/bash
echo "Repairing chowns."
for item in "$@"; do
sudo chown -R ziga:ziga "$item"
done
echo "Setting chmods of directories to 755."
for item in $@; do
sudo find "$item" -type d -exec chmod 755 {} \;
done
echo "Setting chmods of files to 644."
for item in $@; do
sudo find "$item" -type f -exec chmod 644 {} \;
done
echo "Setting chmods of scripts to 744."
for item in $@; do
sudo find "$item" -type f -name "*.sh" -exec chmod 744 {} \;
sudo find "$item" -type f -name "*.pl" -exec chmod 744 {} \;
sudo find "$item" -type f -name "*.py" -exec chmod 744 {} \;
done
我想做的是
bash /home/ziga/scripts/repairPermissions.sh .
时它才有效)。 注意:参数可能在路径中有空格。答案 0 :(得分:4)
a)你在所有情况下循环遍历$ @,你只需要一个循环。 a1)但是find可以为你做这个,你不需要bash循环。 a2)chown可以将多个目录作为参数。
b)根据chw21,删除你拥有的文件的sudo。
c)每个找到的文件/目录有一个exec很贵,请使用xargs。
d)根据chw21,将最后三个发现合并为一个。
#!/bin/bash
echo "Repairing permissions."
sudo chown -R ziga:ziga "$@"
find "$@" -type d -print0 | xargs -0 --no-run-if-empty chmod 755
find "$@" -type f -print0 | xargs -0 --no-run-if-empty chmod 644
find "$@" -type f \
\( -name '*.sh' -o -name '*.pl' -o -name '*.py' \) \
-print0 | xargs -0 --no-run-if-empty chmod 744
这是其他进程的11个exec(sudo,chown,3 * find / xargs / chmod)(如果参数列表很长,xargs会多次执行chmod)。
然而,这会读取目录树四次。操作系统的文件系统缓存应该有所帮助。
编辑:解释为什么使用xargs来回答chepner的评论:
想象一下,有一个包含100个文件的文件夹。
find . -type f -exec chmod 644 {} \;
将执行chmod 100次。
使用find . -type f -print0 | xargs -0 chmod 644
执行xargs一次和chmod一次(如果参数列表很长则使用更多)。
与启动的101个流程相比,这是三个流程。执行三个过程所需的资源和时间(和能量)要少得多。
编辑2:
添加了--no-run-if-empty
作为xargs的选项。请注意,这可能无法移植到所有系统。
答案 1 :(得分:1)
我认为你是ziga
。这意味着在第一个chown
命令之后,您拥有了每个文件和目录,我不明白为什么在此之后您需要sudo
。
你可以很容易地结合最后三个发现:
find "$item" -type f \( -name "*.sh" -o -name "*.py" -o -name "*.pl" \) -exec chmod 744 {} \;
除此之外,我想知道你倾向于找到什么样的破坏权限。例如,chmod
确实知道+X
修饰符,如果用户,群组或其他人中至少有一个已经拥有x
,则该修饰符仅设置x
。
答案 2 :(得分:1)
你可以简化这个:
for item in "$@"; do
对此:
for item; do
没错,for
循环的默认值来自"$@"
。
如果某些目录包含空格,那么这将无法正常工作:
for item in $@; do
再次,用for item; do
替换。所有其他循环都一样。
正如另一个答案所指出的那样,如果您将此脚本作为ziga
运行,那么除了第一个循环外,您可以删除所有sudo
。
答案 3 :(得分:0)
您可以使用成语for item
代替for item in "$@"
我们可以将find的使用减少到一次(使用双循环)。
我假设ziga是你的"$USER"
名字。
使用-d
选项的readarray需要Bash 4.4。
#!/bin/bash
user=$USER
for i
do
# Repairing chowns.
sudo chown -R "$user:$user" "$i"
readarray -d '' -t line< <(sudo find "$i" -print0)
for f in "${line[@]}"; do
# Setting chmods of directories to 755.
[[ -d $f ]] && sudo chmod 755 "$f"
# Setting chmods of files to 644.
[[ -f $f ]] && sudo chmod 644 "$f"
# Setting chmods of scripts to 744.
[[ $f == *.@(sh|pl|py) ]] && sudo chmod 744 "$f"
done
done
如果您有较旧的bash(2.04+),请将readarray行更改为:
while IFS='' read -d '' line; do arr+=("$line"); done < <(sudo find "$i" -print0)
我保持sudo假设在"items"
migth中"$@"
在搜索目录中重复。如果没有重复,可以省略sudo。
每个循环有两个不可避免的外部命令(sudo chown
)。
每个循环只有一个查找。
其他两个外部命令(sudo chmod
)仅减少为完成所需更改所需的命令。
但是shell的工作总是非常缓慢。
因此,速度的提升取决于使用脚本的文件类型。
使用time script
进行一些测试以找出答案。
答案 4 :(得分:0)
受下面列出的一些约束条件限制,我希望类似于以下内容的内容比目前提到的任何内容都要快。我也只使用Bourne shell构造:
#!/bin/sh
set -e
per_file() {
chown ziga:ziga "$1"
test -d "$1" && chmod 755 "$1" || { test -f "$1" && chmod 644 "$1"; }
file "$1" | awk -F: '$2 !~ /script/ {exit 1}' && chmod 744 "$1"
}
if [ "$process_my_file" ]
then
per_file("$1")
exit
fi
export process_my_file=$0
find "$@" -exec $0 {} +
脚本在命令行参数上调用 find (1),并为找到的每个文件调用本身。它通过测试是否存在名为process_my_file
的环境变量来检测重新调用。虽然每次调用shell都有成本,但是如果不遍历文件系统则应该相形见绌。
注意:
set -e
退出第一个错误+
寻找并行执行为每个文件调用文件(1)以确定它的脚本是否比仅测试名称更昂贵,但它也更通用。请注意,awk仅测试描述文本,而不测试文件名。如果文件名包含冒号,则会失败。
您可以将chown从per_file()函数中提升回来,并使用其-R
选项。这可能会更快,因为它是一次执行。这将是一个有趣的实验。
根据您的要求,没有循环,只有一次调用(1)。你还提到了,
现在它只有在我进入所需目录时才有效
但事实并非如此。如果您在命令行中提到目标目录,它也可以使用
$ fix_tree_perms my/favorite/directory
您可以先添加-d
选项进行更改,但我不知道如何更容易使用。