【问题标题】:How to exclude a directory in find . command如何在 find 中排除目录。命令
【发布时间】:2011-05-11 17:40:22
【问题描述】:

我正在尝试对所有 JavaScript 文件运行 find 命令,但如何排除特定目录?

这是我们正在使用的find 代码。

for file in $(find . -name '*.js')
do 
  java -jar config/yuicompressor-2.4.2.jar --type js $file -o $file
done

【问题讨论】:

    标签: linux shell find


    【解决方案1】:

    如果-prune 不适合您,这将:

    find -name "*.js" -not -path "./directory/*"
    

    警告:需要遍历所有不需要的目录。

    【讨论】:

    • 接受的答案中的一个 cmets 指出了问题所在。 -prune 不排除目录本身,它排除了它的内容,这意味着您将在排除目录的输出中得到不需要的行。
    • 很好的答案。我要补充一点,您可以通过将第一个 . 更改为 * 来排除任何级别的目录。所以find -name "*.js" -not -path "*/omitme/*" 会在任何深度级别忽略名为“omitme”的目录中的文件。
    • 它仍然遍历所有不需要的目录。我正在添加我自己的答案。 :-)
    • 但是请注意,只有在您不明确使用 -print 时,prune 选项才有效。
    • 最好说“这是使用 -prune 的替代方法”。建议 -prune 的答案显然没有错,它们只是不是你会这样做的方式。
    【解决方案2】:

    使用-prune 主要。比如要排除./misc

    find . -path ./misc -prune -o -name '*.txt' -print
    

    排除多个目录,或在括号之间。

    find . -type d \( -path ./dir1 -o -path ./dir2 -o -path ./dir3 \) -prune -o -name '*.txt' -print
    

    并且,要在任何级别排除具有特定名称的目录,请使用 -name 主目录而不是 -path

    find . -type d -name node_modules -prune -o -name '*.json' -print
    

    【讨论】:

    • 这对我不起作用,直到我在本地路径前加上 ./,例如./.gitfind 的这种区别对于偶尔的find 用户来说可能并不明显。
    • 既然这是公认的答案,我觉得这里应该提一下,之所以要在最后加上-print是为了防止默认行为,也就是打印修剪目录。 @cycollins 在另一个答案中很好地解释了这一点。
    【解决方案3】:

    我发现以下解决方案比其他建议的解决方案更容易推理:

    find build -not \( -path build/external -prune \) -name \*.js
    # you can also exclude multiple paths
    find build -not \( -path build/external -prune \) -not \( -path build/blog -prune \) -name \*.js
    

    重要提示:您在-path 之后键入的路径必须与find 在不排除的情况下打印的内容完全匹配。如果这句话让您感到困惑,您只需确保在 whole 命令中使用完整路径,如下所示:find <strong>/full/path/</strong> -not \( -path <strong>/full/path/exclude/this</strong> -prune \) ...。如果您想更好地理解,请参阅注释 [1]。

    \(\) 内部是一个表达式,将完全匹配 build/external(参见上面的重要说明),并且在成功时避免遍历下面的任何内容时间>。然后将其分组为带有转义括号的单个表达式,并以 -not 为前缀,这将使 find 跳过与该表达式匹配的任何内容。

    可能有人会问,添加-not 是否不会让-prune 隐藏的所有其他文件重新出现,答案是否定的。 -prune 的工作方式是,一旦到达,该目录下的文件将被永久忽略。

    这来自一个实际的用例,我需要对 Wintersmith 生成的一些文件调用 yui-compressor,但忽略了其他需要按原样发送的文件。


    注意 [1]:如果您想排除 /tmp/foo/bar 并像这样“find /tmp \(...”运行查找,则必须指定 -path /tmp/foo/bar。另一方面,如果您像这样运行cd /tmp; find . \(...,那么您必须指定-path ./foo/bar

    【讨论】:

    • 出色的答案,谢谢。这适用于多个排除项并且是可扩展的(可读的)。先生,您是一位绅士和一位学者。感谢您提供多个排除的示例
    • 如果我想使用 -delete 开关,这不起作用:find . -not \( -path ./CVS -prune \) -type f -mtime +100 -delete find: The -delete action atomatically turns on -depth, but -prune does nothing when -depth is in effect. If you want to carry on anyway, just explicitly use the -depth option.
    • @Janis 你可以使用-exec rm -rf {} \; 代替-delete
    • 通过检查find 的输出,这确实很明显,但它让我感到困惑。如果您在当前目录中搜索(通过指定. 作为搜索路径,或者根本不指定一个),您很可能希望-path 之后的模式以./ 开头,例如:find -not \( -path ./.git -prune \) -type f
    • 此方法的更精确(且与 POSIX 兼容)变体:find searchdir \! \( -type d \( -path './excludedir/*' -o -path './excludedir2/*' -o -path './excludedir3/*' \) -prune \) 后跟任何与您正在寻找的条件相匹配的条件。
    【解决方案4】:

    对于跳过目录的首选语法应该是什么,这里显然有些混淆。

    GNU 意见

    To ignore a directory and the files under it, use -prune
    

    From the GNU find man page

    推理

    -prune 阻止 find 下降到目录中。仅指定 -not -path 仍会进入 skipped 目录,但只要 find 测试每个文件,-not -path 就会为 false。

    -prune 的问题

    -prune 做了它应该做的事情,但在使用它时仍然需要注意一些事情。

    1. find 打印修剪后的目录。

      • TRUE 这是预期的行为,它只是不会陷入其中。为避免完全打印目录,请使用逻辑上省略它的语法。
    2. -prune 仅适用于 -print,不能用于其他操作。

      • 不正确-prune 适用于除-delete 之外的任何操作。 为什么不能和delete一起使用? 要让-delete工作,find需要按照DFS顺序遍历目录,因为-delete会先删除叶子,然后再删除叶子的父节点,等等...但是要指定-prune 才有意义,find 需要点击一个目录并停止对其进行降序处理,这显然对-depth-delete 没有任何意义。

    性能

    我对这个问题的三个最受好评的答案进行了简单测试(将 -print 替换为 -exec bash -c 'echo $0' {} \; 以显示另一个操作示例)。结果如下

    ----------------------------------------------
    # of files/dirs in level one directories
    .performance_test/prune_me     702702    
    .performance_test/other        2         
    ----------------------------------------------
    
    > find ".performance_test" -path ".performance_test/prune_me" -prune -o -exec bash -c 'echo "$0"' {} \;
    .performance_test
    .performance_test/other
    .performance_test/other/foo
      [# of files] 3 [Runtime(ns)] 23513814
    
    > find ".performance_test" -not \( -path ".performance_test/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
    .performance_test
    .performance_test/other
    .performance_test/other/foo
      [# of files] 3 [Runtime(ns)] 10670141
    
    > find ".performance_test" -not -path ".performance_test/prune_me*" -exec bash -c 'echo "$0"' {} \;
    .performance_test
    .performance_test/other
    .performance_test/other/foo
      [# of files] 3 [Runtime(ns)] 864843145
    

    结论

    f10bit's syntaxDaniel C. Sobral's syntax 平均运行时间为 10-25 毫秒。 GetFree's syntax,不使用-prune,耗时865ms。所以,是的,这是一个相当极端的例子,但如果你关心运行时间并且正在做任何远程密集型的事情,你应该使用-prune

    注意Daniel C. Sobral's syntax 在两种-prune 语法中表现更好;但是,我强烈怀疑这是某些缓存的结果,因为切换两者运行的顺序会导致相反的结果,而非修剪版本总是最慢。

    测试脚本

    #!/bin/bash
    
    dir='.performance_test'
    
    setup() {
      mkdir "$dir" || exit 1
      mkdir -p "$dir/prune_me/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/w/x/y/z" \
        "$dir/other"
    
      find "$dir/prune_me" -depth -type d -exec mkdir '{}'/{A..Z} \;
      find "$dir/prune_me" -type d -exec touch '{}'/{1..1000} \;
      touch "$dir/other/foo"
    }
    
    cleanup() {
      rm -rf "$dir"
    }
    
    stats() {
      for file in "$dir"/*; do
        if [[ -d "$file" ]]; then
          count=$(find "$file" | wc -l)
          printf "%-30s %-10s\n" "$file" "$count"
        fi
      done
    }
    
    name1() {
      find "$dir" -path "$dir/prune_me" -prune -o -exec bash -c 'echo "$0"'  {} \;
    }
    
    name2() {
      find "$dir" -not \( -path "$dir/prune_me" -prune \) -exec bash -c 'echo "$0"' {} \;
    }
    
    name3() {
      find "$dir" -not -path "$dir/prune_me*" -exec bash -c 'echo "$0"' {} \;
    }
    
    printf "Setting up test files...\n\n"
    setup
    echo "----------------------------------------------"
    echo "# of files/dirs in level one directories"
    stats | sort -k 2 -n -r
    echo "----------------------------------------------"
    
    printf "\nRunning performance test...\n\n"
    
    echo \> find \""$dir"\" -path \""$dir/prune_me"\" -prune -o -exec bash -c \'echo \"\$0\"\'  {} \\\;
    name1
    s=$(date +%s%N)
    name1_num=$(name1 | wc -l)
    e=$(date +%s%N)
    name1_perf=$((e-s))
    printf "  [# of files] $name1_num [Runtime(ns)] $name1_perf\n\n"
    
    echo \> find \""$dir"\" -not \\\( -path \""$dir/prune_me"\" -prune \\\) -exec bash -c \'echo \"\$0\"\' {} \\\;
    name2
    s=$(date +%s%N)
    name2_num=$(name2 | wc -l)
    e=$(date +%s%N)
    name2_perf=$((e-s))
    printf "  [# of files] $name2_num [Runtime(ns)] $name2_perf\n\n"
    
    echo \> find \""$dir"\" -not -path \""$dir/prune_me*"\" -exec bash -c \'echo \"\$0\"\' {} \\\;
    name3
    s=$(date +%s%N)
    name3_num=$(name3 | wc -l)
    e=$(date +%s%N)
    name3_perf=$((e-s))
    printf "  [# of files] $name3_num [Runtime(ns)] $name3_perf\n\n"
    
    echo "Cleaning up test files..."
    cleanup
    

    【讨论】:

    • 感谢您的精彩分析。关于“我强烈怀疑这是某些缓存的结果”,您可以运行以下命令: sudo sh -c "free && sync && echo 3 > /proc/sys/vm/drop_caches && free" 以清除缓存(请参阅@987654326 @)。
    • 在使用-prune 对这两者进行几次测试后,我可以看出几乎没有任何区别。请记住,哪个命令首先启动会受益于 cpu 性能,后来 cpu warm up > performance drop 会导致轻微的减速(我在每个命令之前都清除了缓存作为@ndemou 的建议)
    • 在上面的@BroSlow 测试脚本中尝试在name1() name2() name3() 之间切换号码以更改执行顺序,从而直观地了解我所说的内容。但在现实生活中,这两者之间并不明显。
    • 掌声。感谢您提供高质量的答案。
    • 你不应该是 -o 这意味着或。所以你在第一步中进行修剪,然后在下一步中忘记它。
    【解决方案5】:

    这是唯一对我有用的。

    find / -name MyFile ! -path '*/Directory/*'
    

    搜索“我的文件”,不包括“目录”。 强调星星 * 。

    【讨论】:

    • 此方法适用于 macOS,而接受的答案则不适用。我知道最初的问题是针对 Linux 的。
    • 注意,可以在命令中连续添加多个! -path '*/Directory/*'来忽略多个目录
    • docker container 中仅适用于sh -c "find..."
    • 非常适合我找到原生 package.json 文件:find . -name package.json ! -path '*/node_modules/*'
    • 简洁明了。适用于 Ubuntu 20.0.4 LTS
    【解决方案6】:

    一种选择是使用 grep 排除所有包含目录名称的结果。例如:

    find . -name '*.js' | grep -v excludeddir
    

    【讨论】:

    • 这会让你的搜索变得很慢
    • 这个对我有用,其他人(使用-prune) - 没有。
    • 大型结果的速度较慢,但​​在较小的集合中很有用。但是如何使用 grep 排除多个目录?当然是这样:find . -name '*.js' | grep -v excludeddir | grep -v excludedir2 | grep -v excludedir3 但可能有一些 grep 方式。
    • 如果你想执行多个 grep,那么你最好把它写成正则表达式:egrep -v '(dir1|dir2|dir3)'。但是,在这个特定的案例研究中,最好排除 find 本身内的目录。
    • 是的,你不需要括号,最好使用 ^ 来确保它与字符串开头的目录名匹配,例如: find 。 -name '*.js' | egrep -v "^\./excludeddir1|^\./excludeddir2"
    【解决方案7】:

    我更喜欢-not 符号……它更易读:

    find . -name '*.js' -and -not -path directory
    

    【讨论】:

    • 对不起,它不起作用。 find 的手册页说:“要忽略目录及其下的文件,请使用 -prune”。
    • 这是错误的。不妨碍 find 进入目录,遍历里面的所有文件。
    • find . -iname '*' -and -not -path './somePath' 不会阻止它进入所述目录。
    • 这帮助了我的 .git 路径 find . -iname '*' -not -path './.git/*'
    • @rane:更具体地说,find . -not -path "*/.git*" 就是你想要的。
    【解决方案8】:

    使用 -prune 选项。所以,类似:

    find . -type d -name proc -prune -o -name '*.js'
    

    “-type d -name proc -prune”只查找名为 proc 的目录以排除。
    '-o' 是一个 'OR' 运算符。

    【讨论】:

    • 这是唯一对我有用的纯“查找”解决方案。我希望排除的目录不在当前工作目录的正下方。
    • 但是,在末尾添加-print 可能会改善结果。 find . -type d -name .hg -prune -o -name data 忽略了(多个).hg 目录的内容,但列出了 .hg 目录本身。使用-print,它只列出了我正在寻找的“数据”目录。
    • 是的,比接受的答案解释得更好。它有效。这是一种发现降价的味道,除了/node_modules/ 下的那些:find . -name node_modules -prune -o -name '*.md' 想要添加额外的东西,比如 -print 没什么错,但至少让一些基本的东西先起作用。
    【解决方案9】:

    -prune 绝对有效,并且是最佳答案,因为它可以防止下降到您要排除的目录。 -not -path 仍然搜索排除的目录,只是不打印结果,如果排除的目录是挂载的网络卷或者您没有权限,这可能是一个问题。

    棘手的部分是find 对参数的顺序非常讲究,所以如果你没有把它们弄得恰到好处,你的命令可能不起作用。参数的顺序一般是这样的:

    find {path} {options} {action}
    

    {path}:把所有路径相关的参数放在最前面,比如. -path './dir1' -prune -o

    {options}:将-name, -iname, etc 作为该组中的最后一个选项时,我获得了最大的成功。例如。 -type f -iname '*.js'

    {action}:使用-prune时,您需要添加-print

    这是一个工作示例:

    # setup test
    mkdir dir1 dir2 dir3
    touch dir1/file.txt; touch dir1/file.js
    touch dir2/file.txt; touch dir2/file.js
    touch dir3/file.txt; touch dir3/file.js
    
    # search for *.js, exclude dir1
    find . -path './dir1' -prune -o -type f -iname '*.js' -print
    
    # search for *.js, exclude dir1 and dir2
    find . \( -path './dir1' -o -path './dir2' \) -prune -o -type f -iname '*.js' -print
    

    【讨论】:

      【解决方案10】:

      这是我用来排除某些路径的格式:

      $ find ./ -type f -name "pattern" ! -path "excluded path" ! -path "excluded path"
      

      我用它来查找不在“.*”路径中的所有文件:

      $ find ./ -type f -name "*" ! -path "./.*" ! -path "./*/.*"
      

      【讨论】:

      • 我试过了,它仍然下放到目录中,所以速度肯定没有提高。
      【解决方案11】:

      有很多很好的答案,只是我花了一些时间来理解命令的每个元素的用途及其背后的逻辑。

      find . -path ./misc -prune -o -name '*.txt' -print
      

      find 将开始在当前目录中查找文件和目录,因此find .

      -o 选项代表逻辑 OR 并将命令的两个部分分开:

      [ -path ./misc -prune ] OR [ -name '*.txt' -print ]
      

      不是 ./misc 目录的任何目录或文件都不会通过第一个测试-path ./misc。但它们将针对第二个表达式进行测试。如果他们的名字对应于*.txt 模式,他们就会被打印出来,因为-print 选项。

      当 find 到达 ./misc 目录时,该目录只满足第一个表达式。所以-prune 选项将应用于它。它告诉 find 命令探索该目录。因此 ./misc 中的任何文件或目录都不会被 find 探索,不会针对表达式的第二部分进行测试,也不会被打印。

      【讨论】:

      • 每个人都有解决方案,但你的解释最好。我坚持首先使用-name 而不是-path。你的解释足以达到我想要的。找 。 -name "*.txt" -print -o -path ./misc -prune
      【解决方案12】:

      -path -prune 方法也适用于路径中的通配符。这是一个查找语句,它将查找服务于多个 git 存储库的 git 服务器的目录,而忽略了 git 内部目录:

      find . -type d \
         -not \( -path */objects -prune \) \
         -not \( -path */branches -prune \) \
         -not \( -path */refs -prune \) \
         -not \( -path */logs -prune \) \
         -not \( -path */.git -prune \) \
         -not \( -path */info -prune \) \
         -not \( -path */hooks -prune \)  
      

      【讨论】:

        【解决方案13】:

        在 Linux Ubuntu 18.04 和 20.04 中测试。

        注意./(或*/,见下文)之前/*(或*,但见下文警告)之后 要排除的文件夹名称是必需的,以便排除dir_to_exclude,以及其中的任何内容!即:

        总结

        这些工作:

        # [my favorite #1] exclude contents of `dir_to_exclude` at the search root
        find -not -path "./dir_to_exclude/*"
        
        # exclude all files & folders beginning with the name `dir_to_exclude` at the
        # search root   
        find -not -path "./dir_to_exclude*"
        
        # [my favorite #2] exclude contents of `dir_to_exclude` at any level within your
        # search path
        find -not -path "*/dir_to_exclude/*"
        
        # exclude all files & folders beginning with the name `dir_to_exclude` at any
        # level within your search path
        find -not -path "*/dir_to_exclude*"
        

        这些也有效,而且更好,因为它们使 find 不会不必要地遍历排除的路径!:
        (这会在速度上产生巨大的差异!参见herehere)。

        find -not \( -path "./dir_to_exclude" -prune \)  # works here but not above
        find -not \( -path "./dir_to_exclude*" -prune \)
        find -not \( -path "./dir_to_exclude/*" -prune \)
        find -not \( -path "*/dir_to_exclude" -prune \)  # works here but not above
        find -not \( -path "*/dir_to_exclude*" -prune \)
        find -not \( -path "*/dir_to_exclude/*" -prune \)
        

        ...但这些不起作用:

        # These do NOT work!
        find -not -path "dir_to_exclude"
        find -not -path "dir_to_exclude/*"
        find -not -path "./dir_to_exclude"
        find -not -path "./dir_to_exclude/"
        

        要使其工作,您必须以./*/开始每个匹配模式,并以/* 或@结束每个匹配模式987654340@,取决于您要达到的目标。

        进一步说明:

        1. [在我看来是最好的] 这很有效!排除您搜索的根目录dir_to_exclude 内的所有文件和文件夹。 请注意,这不包括 dir_to_exclude 内的所有子文件和子文件夹,但不包括 dir_to_exclude 目录本身。
          find -not -path "./dir_to_exclude/*"
          
        2. 还要排除dir_to_exclude 目录本身(以及名称以这些字符开头的任何文件或文件夹)。 警告:这也排除了dir_to_exclude1dir_to_exclude2dir_to_exclude_anyTextHere 等。它排除了仅以文本 dir_to_exclude 开头并且位于您正在搜索的根目录中的任何文件或文件夹。
          find -not -path "./dir_to_exclude*"
          
        3. [最佳,在我看来]递归在您的搜索路径中的任何级别排除此名称的目录。只需将通配符* 也添加到路径的前面,而不是使用. 来指示搜索根目录。
          find -not -path "*/dir_to_exclude/*"
          
        4. 递归排除任何名称​​以字符dir_to_exclude开头的文件或文件夹搜索路径中的任何级别。 (另见上面的警告)。
          find -not -path "*/dir_to_exclude*"
          

        ./ 中,开头的. 表示“在当前目录 中开始”(或在*/ 中,* 是一个通配符,用于拾取任何字符至此),在最后的/* 中,* 是一个通配符,用于在路径字符串中的/ 字符之后提取任何字符。这意味着以下内容:

        1. "./dir_to_exclude/*" 匹配根搜索目录 (./) 中 dir_to_exclude 中的所有子文件和子文件夹,但不匹配目录本身。
        2. "./dir_to_exclude*" 匹配根搜索目录 (./) 中的所有文件和文件夹,包括 dir_to_exclude,以及其中的所有内容,但还有警告,它将匹配任何文件或以字符 dir_to_exclude 开头的文件夹名称。
        3. "*/dir_to_exclude/*" 匹配 搜索路径中任何级别的任何目录 (*/) 中 dir_to_exclude 中的所有子文件和子文件夹,但不匹配目录本身。
        4. "*/dir_to_exclude*" 匹配搜索路径中任何级别 (*/) 中名称以 dir_to_exclude 开头的所有文件和文件夹。

        走得更远

        从那里,我喜欢通过管道发送到grep 以在感兴趣的路径中搜索某些匹配模式。例如:搜索不在dir_to_exclude 目录内且包含desired_file_name.txt 的任何路径:

        # Case-sensitive; notice I use `\.` instead of `.` when grepping, in order to
        # search for the literal period (`.`) instead of the regular expression
        # wildcard char, which is also a period (`.`).
        find -not -path "./dir_to_exclude/*" | grep "desired_file_name\.txt"
        
        # Case-INsensitive (use `-i` with your `grep` search)
        find -not -path "./dir_to_exclude/*" | grep -i "desired_file_name\.txt"
        
        # To make `dir_to_exclude` also case INsensitive, use the `find` `-ipath` option
        # instead of `-path`:
        find -not -ipath "./dir_to_exclude/*" | grep -i "desired_file_name\.txt"
        

        要排除多个匹配模式,只需多次使用-not -path "*/matching pattern/*"。例如:

        # Exclude all ".git" and "..git" dirs at any level in your search path
        find -not -path "*/.git/*" -not -path "*/..git/*"
        

        我将上述示例用作我的sublf alias here 的一部分。这个别名允许我使用 fzf 模糊查找器在 Sublime Text 中快速搜索和打开多个文件。有关最新版本,请参阅上面的链接。

        alias sublf='FILES_SELECTED="$(find -not -path "*/.git/*" -not -path "*/..git/*" \
        | fzf -m)" \
        && echo "Opening these files in Sublime Text:" \
        && echo "$FILES_SELECTED" \
        && subl $(echo "$FILES_SELECTED")'
        

        参考资料:

        1. 【这个问题的主要答案】How to exclude a directory in find . command
        2. https://unix.stackexchange.com/questions/350085/is-it-possible-to-exclude-a-directory-from-the-find-command/350172#350172
        3. https://unix.stackexchange.com/questions/32155/find-command-how-to-ignore-case/32158#32158

        另见:

        1. [我还需要学习和阅读这个]https://www.baeldung.com/linux/find-exclude-paths

        关键字:在 find 命令中排除 dir;不要用 find 搜索路径;不区分大小写的 find 和 grep 命令

        【讨论】:

        • 不适用于 mac
        • @holms,我没有 Mac,但 MacOs manual for find 显示 -not-path 都受支持,所以知道为什么它不起作用,或者如何制作它在 Mac 上工作?
        • 实际上,对我不起作用。原因是“this works”需要替换为find -not -path "./dir_to_exclude*",没有最后一个斜杠。您的版本仍然列出 dir_to_exclude。
        • @emk2203,我明白你的意思,因此我已经大量更新了我的答案。我仍然更喜欢匹配模式"./dir_to_exclude/*",但是,因为"./dir_to_exclude*" 会产生意想不到的副作用,即匹配以字符串dir_to_exclude 开头的任何文件或文件夹,例如dir_to_exclude1dir_to_exclude2、@987654394 @等
        • 我遇到的情况是管道文件名到tardir_to_exclude 仍在输出中,即使没有文件,该目录及其内容仍会被压缩并添加到存档中。我同意您的担忧,但在某些情况下您还必须排除目录或所需操作失败。
        【解决方案14】:

        排除多个目录:

        find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" \)
        

        要添加目录,请添加-o -path "./dirname/*":

        find . -name '*.js' -not \( -path "./dir1" -o -path "./dir2/*" -o -path "./dir3/*"\)
        

        但如果要排除很多目录,也许你应该使用regular expression

        【讨论】:

          【解决方案15】:

          避免打印修剪目录的一个好技巧是在-prune 之后的-or 右侧之后使用-print(也适用于-exec)。例如,...

          find . -path "*/.*" -prune -or -iname "*.j2"
          

          将打印当前目录下所有文件的路径,扩展名为“.j2”,跳过所有隐藏目录。整洁。但它也会打印正在跳过的每个目录的完整路径,如上所述。但是,以下没有,...

          find . -path "*/.*" -prune -or -iname "*.j2" -print
          

          因为逻辑上在-iname 运算符之后和-print 之前有一个隐藏的-and。由于操作的布尔顺序和关联性,这会将其绑定到 -or 子句的右侧部分。但是文档说如果没有指定 -print(或其任何表亲...... -print0 等),则存在隐藏的 -print。那么为什么-or 的左边部分不打印呢?显然(我第一次阅读手册页时并没有理解这一点),如果没有-print - 或-exec ANYWHERE,那就是真的,在这种情况下,-print 在逻辑上散布在周围,这样一切都得到打印。如果甚至在任何子句中表达了一个print 样式的操作,所有那些隐藏的逻辑操作都会消失,您只会得到您指定的内容。现在坦率地说,我可能更喜欢它,但是只有描述性运算符的find 显然什么都不做,所以我想它是有道理的。如上所述,这一切都适用于-exec,因此下面给出了具有所需扩展名的每个文件的完整ls -la 列表,但没有列出每个隐藏目录的第一级,...

          find . -path "*/.*" -prune -or -iname "*.j2" -exec ls -la -- {} +
          

          对我(以及该线程中的其他人)来说,find 语法很快就会变得非常巴洛克,所以我总是加上括号以确保我知道什么绑定到什么,所以我通常创建一个用于类型能力和形成所有这样的陈述......

          find . \( \( ... description of stuff to avoid ... \) -prune \) -or \
          \( ... description of stuff I want to find ... [ -exec or -print] \)
          

          以这种方式将世界分为两部分,很难出错。我希望这会有所帮助,尽管似乎任何人都不太可能阅读到第 30 个以上的答案并投票赞成,但人们可以希望。 :-)

          【讨论】:

            【解决方案16】:

            如果有人正在研究如何一次忽略多个路径。 您可以使用 bash 数组(在 GNU bash 版本 4.4.20(1)-release 上完美运行)

            #!/usr/bin/env bash
            
            # This script helps ignore unnecessary dir paths while using the find command
            
            EXCLUDE_DIRS=(
                "! -path /*.git/*"
                "! -path /*go/*"
                "! -path /*.bundle/*"
                "! -path /*.cache/*"
                "! -path /*.local/*"
                "! -path /*.themes/*"
                "! -path /*.config/*"
                "! -path /*.codeintel/*"
                "! -path /*python2.7/*"
                "! -path /*python3.6/*"
                "! -path /*__pycache__/*"
            )
            find $HOME -type f ${EXCLUDE_DIRS[@]}
            
            # if you like fzf
            
            find $HOME -type f ${EXCLUDE_DIRS[@]} | fzf --height 40% --reverse
            
            

            另外由于某种原因,您将无法忽略 /bin/ 目录路径。

            【讨论】:

            • 我喜欢这个不使用-prune
            • 要忽略 __pycache__(在任何嵌套目录及其所有内容中)我需要用 ./*__pycache__/* 替换 /*__pycache__/*
            • 还有一个可行的方法是将不带引号的排除目录的参数存储在文件中,每行一个,然后使用find $HOME -type f $(&lt; ~/excludelist) 调用它。文件的第一行是! -path /*.git/*,第二行是! -path /*.mozilla/*,依此类推。
            【解决方案17】:

            对于可行的解决方案(在 Ubuntu 12.04(Precise Pangolin)上测试)...

            find ! -path "dir1" -iname "*.mp3"
            

            将在当前文件夹和除dir1子文件夹之外的子文件夹中搜索MP3文件。

            用途:

            find ! -path "dir1" ! -path "dir2" -iname "*.mp3"
            

            ...排除 dir1 AND dir2

            【讨论】:

            • 对我不起作用。以上任何一个答案都没有。红帽。
            【解决方案18】:
            find . \( -path '.**/.git' -o -path '.**/.hg' \) -prune -o -name '*.js' -print
            

            上面的示例查找当前目录下的所有*.js文件,不包括文件夹.git.hg,无论.git.hg文件夹有多深。

            注意:这也有效:

            find . \( -path '.*/.git' -o -path '.*/.hg' \) -prune -o -name '*.js' -print
            

            但我更喜欢 ** 表示法,以便与其他一些工具保持一致,这些工具在此处不属于主题。

            【讨论】:

              【解决方案19】:
              find -name '*.js' -not -path './node_modules/*' -not -path './vendor/*'
              

              似乎和

              一样
              find -name '*.js' -not \( -path './node_modules/*' -o -path './vendor/*' \)
              

              而且更容易记住 IMO。

              【讨论】:

                【解决方案20】:

                您还可以使用正则表达式来包含/排除某些文件/dirs 您的搜索使用如下内容:

                find . -regextype posix-egrep -regex ".*\.(js|vue|s?css|php|html|json)$" -and -not -regex ".*/(node_modules|vendor)/.*" 
                

                这只会为您提供所有 js、vue、css 等文件,但不包括 node_modulesvendor 文件夹中的所有文件。

                【讨论】:

                  【解决方案21】:

                  以前的答案在 Ubuntu 上都不好。 试试这个:

                  find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*"
                  

                  我找到了这个here

                  【讨论】:

                  • 我看不出任何超过 100 分的答案不能在 Ubuntu 上运行的理由。
                  • find 在所有 Linux 发行版上到处都是相同的实现——来自 GNU 项目的那个。唯一的区别可能是版本。但过去十年的变化并没有那么大,除了权限匹配。
                  【解决方案22】:

                  您可以使用 prune 选项来实现此目的。例如:

                  find ./ -path ./beta/* -prune -o -iname example.com -print
                  

                  或者逆grep“grep -v”选项:

                  find -iname example.com | grep -v beta
                  

                  您可以在Linux find command exclude directories from searching中找到详细说明和示例。

                  【讨论】:

                  • grep 解决方案是唯一排除所有同名目录的解决方案。尝试排除“node_modules”时非常有用。
                  • @bmacnaughton - 不正确!我来这里是专门为了排除“node_modules”,在阅读了许多很好的答案后,我选择了find . -type f -print -o -path "*/node_modules" -prune ...使用通配符在任何级别跳过“node_modules”;在第一个替代方案-type f -print 上使用-print 只会打印该部分,因此“node_modules”目录本身未列出。 (也可以反转:find . -path "*/node_modules" -prune -o -type f -print
                  • */ 在那里做什么。您要排除的确切文件是什么。ypu 是否将其用作通配符?
                  • @StephenP,感谢您指出这一点;我从中了解了使用./node_modules*/node_modules 之间的区别。对于我的情况,node_modules 仅存在于我开始搜索的目录中(以及在该node_modules 目录下),我可以使用find . -type f -print -o -path "./node_modules" -prune,因为在任何其他目录下都不会有node_modules 目录.
                  • @SijuV - 在我搜索的目录中有一个node_modules 子目录,但也有一些子目录有自己的 node_modules ...使用./node_modules仅匹配当前目录.下的子目录node_modules并进行修剪;使用*/node_modules 匹配并修剪任意深度的目录,因为* 作为glob 匹配任何前导路径前缀,例如./test5/main/node_modules,而不仅仅是./ 前缀。 * 是一个通配符,但作为一个全局而不是一个正则表达式。
                  【解决方案23】:
                  find . -name '*.js' -\! -name 'glob-for-excluded-dir' -prune
                  

                  【讨论】:

                  • 无法让这个工作。 find ~/Projects -name '*.js' -\! -name 'node_modules' -prune 仍在打开路径中带有 node_modules 的文件
                  • @mpen ,从stackoverflow.com/questions/4210042/…,我了解到你想要的语法是find ~/Projects -path ~/Projects/node_modules -prune -o -name '*.js' -print。如果要打印目录,该路径的名称必须与 find 将打印的内容完全匹配。
                  【解决方案24】:

                  TLDR:了解您的根目录并使用-path &lt;excluded_path&gt; -prune -o 选项从那里定制您的搜索。不要在排除路径的末尾包含尾随 /

                  例子:

                  find / -path /mnt -prune -o -name "*libname-server-2.a*" -print


                  为了有效地使用find,我相信对你的文件系统目录结构有一个很好的了解是很有必要的。在我的家用计算机上,我有多 TB 硬盘驱动器,其中大约一半的内容使用 rsnapshot(即 rsync)备份。虽然备份到物理上独立(重复)的驱动器,但它安装在我的系统根 (/) 目录下:/mnt/Backups/rsnapshot_backups/

                  /mnt/Backups/
                  └── rsnapshot_backups/
                      ├── hourly.0/
                      ├── hourly.1/
                      ├── ...
                      ├── daily.0/
                      ├── daily.1/
                      ├── ...
                      ├── weekly.0/
                      ├── weekly.1/
                      ├── ...
                      ├── monthly.0/
                      ├── monthly.1/
                      └── ...
                  

                  /mnt/Backups/rsnapshot_backups/ 目录目前占用约 2.9 TB,有约 60M 文件和文件夹;简单地遍历这些内容需要时间:

                  ## As sudo (#), to avoid numerous "Permission denied" warnings:
                  
                  time find /mnt/Backups/rsnapshot_backups | wc -l
                  60314138    ## 60.3M files, folders
                  34:07.30    ## 34 min
                  
                  time du /mnt/Backups/rsnapshot_backups -d 0
                  3112240160  /mnt/Backups/rsnapshot_backups    ## 3.1 TB
                  33:51.88    ## 34 min
                  
                  time rsnapshot du    ## << more accurate re: rsnapshot footprint
                  2.9T    /mnt/Backups/rsnapshot_backups/hourly.0/
                  4.1G    /mnt/Backups/rsnapshot_backups/hourly.1/
                  ...
                  4.7G    /mnt/Backups/rsnapshot_backups/weekly.3/
                  2.9T    total    ## 2.9 TB, per sudo rsnapshot du (more accurate)
                  2:34:54          ## 2 hr 35 min
                  

                  因此,每当我需要在我的/(根)分区上搜索文件时,我都需要处理(尽可能避免)遍历我的备份分区。


                  示例

                  在此线程 (How to exclude a directory in find . command) 中建议的各种方法中,我发现使用已接受答案的搜索速度要快得多 - 有一些注意事项。

                  解决方案 1

                  假设我想查找系统文件libname-server-2.a,但我不想搜索我的rsnapshot 备份。要快速查找系统文件,请使用排除路径 /mnt(即,使用 /mnt,而不是 /mnt/,或 /mnt/Backups,或...):

                  ## As sudo (#), to avoid numerous "Permission denied" warnings:
                  
                  time find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
                  /usr/lib/libname-server-2.a
                  real    0m8.644s              ## 8.6 sec  <<< NOTE!
                  user    0m1.669s
                   sys    0m2.466s
                  
                  ## As regular user (victoria); I also use an alternate timing mechanism, as
                  ## here I am using 2>/dev/null to suppress "Permission denied" warnings:
                  
                  $ START="$(date +"%s")" && find 2>/dev/null / -path /mnt -prune -o \
                      -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
                      TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
                  /usr/lib/libname-server-2.a
                  find command took 3 sec     ## ~3 sec  <<< NOTE!
                  

                  ... 只需几秒钟即可找到该文件,而这需要 much 更长的时间(似乎递归遍历所有“排除”目录):

                  ## As sudo (#), to avoid numerous "Permission denied" warnings:
                  
                  time find / -path /mnt/ -prune -o -name "*libname-server-2.a*" -print
                  find: warning: -path /mnt/ will not match anything because it ends with /.
                  /usr/lib/libname-server-2.a
                  real    33m10.658s            ## 33 min 11 sec (~231-663x slower!)
                  user    1m43.142s
                   sys    2m22.666s
                  
                  ## As regular user (victoria); I also use an alternate timing mechanism, as
                  ## here I am using 2>/dev/null to suppress "Permission denied" warnings:
                  
                  $ START="$(date +"%s")" && find 2>/dev/null / -path /mnt/ -prune -o \
                      -name "*libname-server-2.a*" -print; END="$(date +"%s")"; \
                      TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
                  /usr/lib/libname-server-2.a
                  find command took 1775 sec    ## 29.6 min
                  

                  解决方案 2

                  此线程中提供的其他解决方案 (SO#4210042) 也表现不佳:

                  ## As sudo (#), to avoid numerous "Permission denied" warnings:
                  
                  time find / -name "*libname-server-2.a*" -not -path "/mnt"
                  /usr/lib/libname-server-2.a
                  real    33m37.911s            ## 33 min 38 sec (~235x slower)
                  user    1m45.134s
                   sys    2m31.846s
                  
                  time find / -name "*libname-server-2.a*" -not -path "/mnt/*"
                  /usr/lib/libname-server-2.a
                  real    33m11.208s            ## 33 min 11 sec
                  user    1m22.185s
                   sys    2m29.962s
                  

                  总结 |结论

                  使用“解决方案 1”中说明的方法

                  find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
                  

                  ... -path <excluded_path> -prune -o ...
                  

                  请注意,每当您将尾随 / 添加到排除路径时,find 命令然后递归地进入(所有这些)/mnt/* 目录——在我的情况下,由于 /mnt/Backups/rsnapshot_backups/* 子目录,另外包括约 2.9 TB 的文件搜索!通过不附加尾随 /,搜索应该几乎立即完成(在几秒钟内)。

                  “解决方案 2”(... -not -path &lt;exclude path&gt; ...) 同样似乎递归搜索排除的目录 - 不返回排除的匹配项,但不必要地消耗搜索时间。


                  rsnapshot 备份中搜索:

                  在我的每小时/每天/每周/每月rsnapshot 备份之一中查找文件):

                  $ START="$(date +"%s")" && find 2>/dev/null /mnt/Backups/rsnapshot_backups/daily.0 -name '*04t8ugijrlkj.jpg'; END="$(date +"%s")"; TIME="$((END - START))"; printf 'find command took %s sec\n' "$TIME"
                  /mnt/Backups/rsnapshot_backups/daily.0/snapshot_root/mnt/Vancouver/temp/04t8ugijrlkj.jpg
                  find command took 312 sec   ## 5.2 minutes: despite apparent rsnapshot size
                                              ## (~4 GB), it is in fact searching through ~2.9 TB)
                  

                  排除嵌套目录:

                  在这里,我想排除一个嵌套目录,例如/mnt/Vancouver/projects/ie/claws/data/*/mnt/Vancouver/projects/ 搜索时:

                  $ time find . -iname '*test_file*'
                  ./ie/claws/data/test_file
                  ./ie/claws/test_file
                  0:01.97
                  
                  $ time find . -path '*/data' -prune -o -iname '*test_file*' -print
                  ./ie/claws/test_file
                  0:00.07
                  

                  旁白:在命令末尾添加-print 会抑制排除目录的打印输出:

                  $ find / -path /mnt -prune -o -name "*libname-server-2.a*"
                  /mnt
                  /usr/lib/libname-server-2.a
                  
                  $ find / -path /mnt -prune -o -name "*libname-server-2.a*" -print
                  /usr/lib/libname-server-2.a
                  

                  【讨论】:

                  • 导致find 变慢的不是文件的大小,而是它必须检查的目录条目数。所以如果你有很多很多的小文件(尤其是如果它们都是多重链接的!)比你只有几个千兆字节的文件要糟糕得多。
                  • @TobySpeight:好点。我提到了搜索空间大小来表示比例,其中也包含许多文件。使用sudo ls -R / | wc -l 快速搜索根目录 (/) 表示大约 7650 万个文件(除了“非配置”系统文件之外,大部分文件都已备份); /mnt/Vancouver/ls -R | wc -l 表示~235万个文件; /home/victoria/ 包含 0.668M 文件。
                  【解决方案25】:

                  以下命令有效:

                  find . -path ./.git -prune -o -print
                  

                  如果您对查找有问题,请使用-D tree 选项查看表达式分析信息。

                  find -D tree . -path ./.git -prune -o -print
                  

                  -D all,查看所有执行信息。

                  find -D all . -path ./.git -prune -o -print
                  

                  【讨论】:

                    【解决方案26】:

                    这适合我在 Mac 上使用:

                    find . -name *.php -or -path "./vendor" -prune -or -path "./app/cache" -prune
                    

                    它将排除vendorapp/cache 目录以查找后缀为php 的搜索名称。

                    【讨论】:

                    • 最好在 '*.php' 周围加上单引号,否则您将找不到所需的内容。
                    【解决方案27】:

                    我使用findxgettext 提供文件列表,并希望省略特定目录及其内容。我尝试了-path-prune 的许多排列组合,但无法完全排除我想要删除的目录。

                    虽然我能够忽略我想要忽略的目录的 contents,但 find 然后将目录本身作为结果之一返回,这导致 xgettext 结果崩溃(不接受目录;只接受文件)。

                    我的解决方案是简单地使用grep -v 跳过我不希望出现在结果中的目录:

                    find /project/directory -iname '*.php' -or -iname '*.phtml' | grep -iv '/some/directory' | xargs xgettext
                    

                    find 是否存在 100% 有效的论点,我无法确定。在头痛之后使用grep 是一种快速简便的解决方案。

                    【讨论】:

                      【解决方案28】:

                      对于那些在旧版本 UNIX 上无法使用 -path-not 的人

                      在 SunOS 5.10 bash 3.2 和 SunOS 5.11 bash 4.4 上测试

                      find . -type f -name "*" -o -type d -name "*excluded_directory*" -prune -type f
                      

                      【讨论】:

                      • 可以通过多个指定目录。
                      【解决方案29】:

                      how-to-use-prune-option-of-find-in-shLaurence Gonsalves 关于-prune 工作原理的绝佳答案。

                      这是通用的解决方案:

                      find /path/to/search                    \
                        -type d                               \
                          \( -path /path/to/search/exclude_me \
                             -o                               \
                             -name exclude_me_too_anywhere    \
                           \)                                 \
                          -prune                              \
                        -o                                    \
                        -type f -name '*\.js' -print
                      

                      为避免多次输入/path/to/seach/,请将find 包装在pushd .. popd 对中。

                      pushd /path/to/search;                  \
                      find .                                  \
                        -type d                               \
                          \( -path ./exclude_me               \
                             -o                               \
                             -name exclude_me_too_anywhere    \
                           \)                                 \
                          -prune                              \
                        -o                                    \
                        -type f -name '*\.js' -print;         \
                       popd
                      

                      【讨论】:

                      • stackoverflow.com/questions/4210042/…,我了解到用于-path 的语法必须与find 打印目录的名称相匹配,例如find . -path ./.git -prune -o -print 或@ 987654332@ 一些答案只是说-path somedir 不幸的是,它不够准确,没有用处。
                      【解决方案30】:

                      我尝试了上面的命令,但没有一个使用“-prune”的命令对我有用。 最后我用下面的命令试了一下:

                      find . \( -name "*" \) -prune -a ! -name "directory"
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2023-04-02
                        • 1970-01-01
                        • 1970-01-01
                        • 2011-12-12
                        • 2021-10-01
                        • 1970-01-01
                        • 2013-09-14
                        • 1970-01-01
                        相关资源
                        最近更新 更多