查找排除文件中列出的路径的命令

查找排除文件中列出的路径的命令

我需要从命令中排除一堆路径find。例如:

find "$(pwd)" -not \( \
 -path "*/.git"\
 -o -path "*/.git/*"\
 -o -path "*/.vscode"\
 -o -path "*/.vscode/*"\
 -o -path "*/node_modules"\
 -o -path "*/node_modules/*"\
 -o -path "*/Image"\
 -o -path "*/Image/*"\
 -o -path "*/Rendered"\
 -o -path "*/Rendered/*"\
 -o -path "*/iNotebook"\
 -o -path "*/iNotebook/*"\
 -o -path "*/GeneratedTest"\
 -o -path "*/GeneratedTest/*"\
 -o -path "*/GeneratedOutput"\
 -o -path "*/GeneratedOutput/*"\
 -o -path "*/*_files" \) -type d

但是,我想从文本文件中读取这些路径,而不是在命令行上列出所有路径。我怎样才能做到这一点?

答案1

构造一个稍后在调用中使用的数组find。以下脚本从其标准输入读取换行符分隔的路径模式并调用find

#!/bin/sh

set --

while IFS= read -r path; do
    set -- "$@" -o -path "$path"
done

shift   # remove initial "-o" from $@

find . -type d ! '(' "$@" ')'

你会运行这个

./script.sh <paths.txt

哪里paths.txt可能看起来像

*/.git
*/.git/*
*/.vscode
*/.vscode/*
*/node_modules
*/node_modules/*
*/Image
*/Image/*
*/Rendered
*/Rendered/*
*/iNotebook
*/iNotebook/*
*/GeneratedTest
*/GeneratedTest/*
*/GeneratedOutput
*/GeneratedOutput/*
*/*_files

或者,因为您的路径模式基本上都是目录名称:

#!/bin/sh

set --

while IFS= read -r dirname; do
    set -- "$@" -o '(' -name "$dirname" -prune ')'
done

shift   # remove initial "-o" from $@

find . -type d ! '(' "$@" ')'

模式文件包含

.git
.vscode
node_modules
Image
Rendered
iNotebook
GeneratedTest
GeneratedOutput
*_files

代码的这种变体find甚至会停止下降到与文件中的模式匹配的目录,而第一个脚本(以及您的代码)将-path针对排除的目录中的所有内容测试模式,无论您是否不是对这些路径以下的任何内容感兴趣。

答案2

您可以使用grepandfind-exec根据路径列表(作为正则表达式或固定字符串)过滤文件。调整您的示例,创建一个名为paths包含的文件

/.git$
/.git/
/.vscode$
/.vscode/
/node_modules$
/node_modules/
/Image$
/Image/
/Rendered$
/Rendered/
/iNotebook$
/iNotebook/
/GeneratedTest$
/GeneratedTest/
/GeneratedOutput$
/GeneratedOutput/
/.*_files$

然后运行

find /your/search/path -type d ! -exec sh -c "echo {} | grep -q -f paths" \; -print

这会查找 下的目录/your/search/path,并且对于它找到的每个目录,用于确定它是否与;grep中的模式匹配。paths如果没有,则打印它。这是为了作为扩展的基础;如果您只关心与文件中的模式不匹配的目录路径,没有路径覆盖多行,您可以使用单个grep调用对输出进行后处理:

find /your/search/path -type d | grep -v -f paths

如果您真的对某些路径根本不感兴趣(IE您的模式始终匹配目录名称,然后匹配该目录下的所有内容),您可以通过修剪使事情变得更简单:

find /your/search/path -type d \( -exec sh -c "echo {} | grep -q -f paths" \; -prune -o -print \)

路径中包含以下内容:

/.git$
/.vscode$
/node_modules$
/Image$
/Rendered$
/iNotebook$
/GeneratedTest$
/GeneratedOutput$
/.*_files$

答案3

可以做的是使用构建命令awk并将其find作为“包装器”脚本或 shell 函数中的变量传递给

p=$( awk '{printf "-not -path %s ",$0}' "$1" )
find "$PWD"  $p -type d

并将其称为./find_wrapper.sh paths.txt,其中path.txt是引用路径的列表。

'*/.git'
'*/.git/*'
'*/.vscode'
'*/.vscode/*'
'*/node_modules'
'*/node_modules/*'
'*/Image'
...

为什么这样做?构建一整行的原因awk是因为没有理由在脚本中这样做 -\行延续是为了使命令看起来更有组织性,但从功能上来说它没有任何优势。$p没有被引用,因为我们实际上想要在这里进行分词。否则find将其视为一个巨大的字符串,而不是单独的标志和参数。至于单引号,那就是以避免全局双引号中的效果。

或者作为管道

awk '{printf "-not -path %s ",$0}' "$1" | xargs -L 1  find "$PWD" -type d 

相关内容