优化bash脚本(for循环,权限,......)

时间:2016-11-03 05:47:28

标签: linux bash shell sh

我很久以前制作了这个剧本(非常快...... :))并经常使用它,但现在我对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

我想做的是

  1. 减少for循环次数
  2. 减少查找语句的数量(我知道最后三个可以组合成一个,但我想知道它是否可以进一步减少)
  3. 使脚本接受当前目录以外的参数,并可能接受多个参数(现在只有当我进入所需的目录然后调用bash /home/ziga/scripts/repairPermissions.sh .时它才有效)。 注意:参数可能在路径中有空格。

5 个答案:

答案 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)检测脚本
  • chown (1)未递归调用
  • 未遵循符号链接

为每个文件调用文件(1)以确定它的脚本是否比仅测试名称更昂贵,但它也更通用。请注意,awk仅测试描述文本,而不测试文件名。如果文件名包含冒号,则会失败。

您可以将chown从per_file()函数中提升回来,并使用其-R选项。这可能会更快,因为它是一次执行。这将是一个有趣的实验。

根据您的要求,没有循环,只有一次调用(1)。你还提到了,

  

现在它只有在我进入所需目录时才有效

但事实并非如此。如果您在命令行中提到目标目录,它也可以使用

$ fix_tree_perms my/favorite/directory

您可以先添加-d选项进行更改,但我不知道如何更容易使用。