【问题标题】:Speed up bash script that uses several find commands加速使用多个查找命令的 bash 脚本
【发布时间】:2012-10-30 07:44:36
【问题描述】:

我有一个 bash 脚本来将项目的一些文件添加到 git,然后同步该分支,随着文件数量的增加,我注意到脚本变得慢得多,所以我想知道我是否以正确的方式进行。

这是添加文件的脚本部分:

echo "Adding files..."
find . -name '*.js' -exec git add {} \;
find . -name '*.html' -exec git add {} \;
find . -name '*.css' -exec git add {} \;
find . -name '*.py' -exec git add {} \;
find . -name '*.txt' -exec git add {} \;
find . -name '*.jpg' -exec git add {} \;
find . -name '*.sh' -exec git add {} \;

echo "Commit"
git commit -m "'$1'"

我不确定一次调用 find 是否会比使用所有这些单独的命令更快,但我这样做是为了更简单地删除某些类型的文件或添加新文件。

我非常感谢任何提高效率的建议,以不同的方式使用命令或使用不同的命令是完全可以接受的答案。

【问题讨论】:

    标签: linux performance git bash find


    【解决方案1】:

    如果 git 支持在一个命令中添加多个文件,您可以做的最简单的事情是使用 + 后缀 -exec

    find . -name '*.js' -exec git add {} \+
    

    这会收集大量文件并将它们全部传递给一个命令行中的命令。

    那么会执行的是:

    git add a.js b.js c.js d.js
    

    而不是

    git add a.js
    git add b.js
    git add c.js
    git add d.js
    

    如果您要处理成百上千个文件,这将对执行时间产生很大影响。

    要将所有文件模式组合成一个 find 命令,请使用 find 的“或”运算符:

    find . \( -name '*.js' -o   \
              -name '*.html' -o \
              -name '*.css' -o  \
              -name '*.py' -o   \
              -name '*.txt' -o  \
              -name '*.jpg' -o  \
              -name '*.sh' \) -exec git add {} +
    

    () 之前的 \ 用于保护它们免受其特殊外壳含义的影响。您同样可以使用引号:'('')'

    find 有一些复杂的选项,学习它们并熟悉它们需要一些麻烦,但是这些年来我通过能够摆脱复杂的 @ 为自己节省了很多精力987654334@ 命令,而不是通过 grep 和 awk 等来过滤文件名。

    我目前最喜欢扫描 maven/subversion java 项目同时忽略不感兴趣的文件的模式之一是:

    find . \( \( \( -iname .svn -o -iname target -o -iname classes \) -type d -prune -false \) -o \( <your filter expression> \) \) -exec grep -li xxx {} +
    

    【讨论】:

    • 另外,您可以通过xargs 管道输出,最好将find 的开关-print0-0 组合为xargs,这使它们使用以空值结尾的字符串,所以您不必担心转义空间和类似的东西。
    • 非常感谢您的详细解释,我现在正在实施它。
    • 我使用您在此处显示的代码实现了它,但我收到一条错误消息“find: missing arguments to '-exec'”,知道我错过了什么吗?
    • @jeruki:在相当现代的 linux 上? (不到 10 岁?)也许你需要转义 + 符号。很难说
    • 脚本是从centos 6和带有cygwin的windows机器上运行的,我猜是第二个问题,但是使用xargs修复了它
    【解决方案2】:
    find . \( -name '*.js'   -o \
              -name '*.html' -o \
              -name '*.css'  -o \
              -name '*.py'   -o \
              -name '*.txt'  -o \
              -name '*.jpg'  -o \
              -name '*.sh'   \) -exec git add {} +
    

    这意味着你只扫描一次目录结构,这是加速'multiple finds'的主要方式;您将“多个”替换为“一个”。 + 是 POSIX 2008 对 find 的补充,但它的行为更像 xargs 本身。如果您无法使用它,请考虑使用-printxargs(或者,如果您的名称中可能有空格并且您有GNU findxargs,那么-print0 和@987654331 @,但如果你有它们,你(可能 - 但见评论)也有 + 符号)。

    【讨论】:

    • 我试过了,发现我的 bash 版本不支持 + in find 所以我使用了 xargs -0 并且它现在工作正常,谢谢
    【解决方案3】:

    如果你

    • 拥有 Bash 4
    • 仅按名称搜索(不按其他条件)

    你也可以用这个:

    shopt -s globstar
    git add **/*.{js,html,css,py,txt,jpg,sh}
    


    笔记:
    1. 大括号扩展在文件名扩展之前执行,所以这相当于写

      git add **/*.js **/*.html etc...
      
    2. globstar 通过关键字 ** 启用递归文件名扩展。

    【讨论】:

      【解决方案4】:

      git add 命令可以在没有任何其他 shell 脚本的情况下执行此操作。

      git add -- '*.js' '*.html' '*.css' ...
      

      【讨论】:

      • 这似乎是最清晰的选择,这是否也添加了子目录?
      • 好的,我通过试验/错误找出了递归部分,但后来意识到,当它没有找到具有扩展名之一的单个文件时,它会因错误而失败并中止操作,是否存在即使模式不匹配,有一种方法告诉它继续吗?
      • 看起来--ignore-errors 应该为你做这件事,但我还没有测试过。
      • 我试过了,但没用,可能忽略错误并不能忽略所有类型的错误
      【解决方案5】:

      这可能会更快:

      F='\.js$|\.html$|\.css$|\.py$|\.txt$|\.jpg$|\.sh$'
      find . | egrep $F | xargs git add
      

      如果您希望文件名中有空格或其他特殊字符,则可以使用它的一些变体。

      【讨论】:

      • 如果您使用类似的东西,请不要忘记尾随的 $ 和前导 \。我犯了那个错误并丢失了我的 Mercurial 存储库的历史记录:(
      • 如果你在前面花点功夫学习如何直接用find做这种过滤,那么你可以使用-exec选项来执行你需要的命令,难度用“文件名中的空格或其他特殊字符”消失。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-08
      • 1970-01-01
      • 2015-08-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多