使用排除列表在 bash 中使用 find 查找目录
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8139523/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Finding directories with find in bash using a exclude list
提问by Adesso
now before you think, "this has been done before" please read on.
现在,在您认为“以前已经这样做过”之前,请继续阅读。
Like most of the people trying to do a find bash script you end up hard-coding the script to a single line command, but end up editing the thing over the following months/years so often that you wish in the end you did it right the first time.
像大多数尝试执行 find bash 脚本的人一样,您最终将脚本硬编码为单行命令,但最终在接下来的几个月/几年内经常编辑该内容,以至于您最终希望自己做对了第一次。
I am writing a little backup program right now to do backups of directories and need to find them, against a list of directorie's that needs to be excluded. Easier said than done. Let me set the stage:
我现在正在编写一个小备份程序来备份目录并需要根据需要排除的目录列表找到它们。说起来容易做起来难。让我来搭建舞台:
#!/bin/bash
BasePath="/home/adesso/baldar"
declare -a Iggy
Iggy=( "/cgi-bin"
"/tmp"
"/test"
"/html"
"/icons" )
IggySubdomains=$(printf ",%s" "${Iggy[@]}")
IggySubdomains=${IggySubdomains:1}
echo $IggySubdomains
exit 0
Now at the end of this you get /cgi-bin,/tmp,/test,/html,/iconsThis proves that the concept works, but now to take it a bit further I need to use find to search the BasePathand search only one level deep for all subdirectories and exclude the list of subdirectories in the array...
现在最后你得到/cgi-bin,/tmp,/test,/html,/icons这证明这个概念是有效的,但现在更进一步,我需要使用 find 来搜索BasePath并搜索所有子目录只有一层深,并排除数组中的子目录列表...
If I type this by hand it would be:
如果我手动输入,它将是:
find /var/www/* \( -path '*/cgi-bin' -o -path '*/tmp' -o -path '*/test' -o -path '*/html' -o -path '*/icons' \) -prune -type d
And should I maybe want to loop into each subdirectory and do the same... I hope you get my point.
我是否应该循环到每个子目录并做同样的事情...我希望你明白我的意思。
So What I am trying to do seem possible, but I have a bit of a problem, printf ",%s"doesn't like me using all those find -path or -o options. Does this mean I have to use eval again?
所以我试图做的事情似乎是可能的,但我有一点问题,printf ",%s"不喜欢我使用所有这些 find -path 或 -o 选项。这是否意味着我必须再次使用 eval?
I am trying to use the power of bash here, and not some for loop. Any constructive input would be appreciated.
我试图在这里使用 bash 的功能,而不是一些 for 循环。任何建设性的意见将不胜感激。
采纳答案by sorpigal
Try something like
尝试类似
find /var/www/* \( -path "${Iggy[0]}" $(printf -- '-o -path "*%s" ' "${Iggy[@]:1}") \) -prune -type d
and see what happens.
看看会发生什么。
EDIT: added the leading * to each path as in your example.
编辑:如您的示例所示,将前导 * 添加到每个路径。
And here's a complete solution based on your description.
这是基于您的描述的完整解决方案。
#!/usr/bin/env bash
basepath="/home/adesso/baldar"
ignore=("/cgi-bin" "/tmp" "/test" "/html" "/icons")
find "${basepath}" -maxdepth 1 -not \( -path "*${ignore[0]}" $(printf -- '-o -path "*%s" ' "${ignore[@]:1}") \) -not -path "${basepath}" -type d
Subdirectories of $basepath excluding those listed in $ignore, presuming at least two in $ignore (fixing that is not hard).
$basepath 的子目录,不包括 $ignore 中列出的目录,假设 $ignore 中至少有两个(修复并不难)。
回答by Charles Duffy
The existing answers are buggy when given directory names that contain literal whitespace. The safe and robust practice is to use a loop. If your concern is leveraging "the power of bash" -- I'd argue that a robust solution is more powerful than a buggy one. :)
当给定包含文字空格的目录名称时,现有答案有问题。安全可靠的做法是使用循环。如果您关心的是利用“bash 的力量”——我认为一个强大的解决方案比一个有缺陷的解决方案更强大。:)
BasePath="/home/adesso/baldar"
declare -a Iggy=( "/cgi-bin" "/tmp" "/test" "/html" "/icons" )
find_cmd=( find "$BasePath" '(' )
## This is the conventional approach:
# for x in "${Iggy[@]}"; do
# find_cmd+=( -path "*${x}" -o )
#done
## This is the unconventional, only-barely-safe approach
## ...used only to avoid looping:
printf -v find_cmd_str ' -path "*"%q -o ' "${Iggy[@]}"
find_cmd_str=${find_cmd_str%" -o "}
eval "find_cmd+=( $find_cmd_str )"
find_cmd=( "${find_cmd[@]:0:${#find_cmd[@]} - 1}"
# and add the suffix
find_cmd+=( ')' -prune -type d )
# ...finally, to run the command:
"${find_cmd[@]}"
回答by Adesso
FIND="$(which find --skip-alias)"
BasePath="/home/adesso/baldar"
Iggy=( "/cgi-bin"
"/tmp"
"/test"
"/html"
"/icons" )
SubDomains=( $(${FIND} ${BasePath}/* -maxdepth 0 -not \( -path "*${Iggy[0]}" $(printf -- '-o -path "*%s" ' "${Iggy[@]:1}") \) -type d) )
echo ${SubDomains[1]}
Thanks to @Sorpigal I have a solution. I ended up nesting the command substitution so I can use the script in a cron, and finally added the Array definition around all of it. A known problem would be a directory containing a space in the name. This however has been solved, so trying to keep it simple, I think this answers my question.
感谢@Sorpigal 我有一个解决方案。我最终嵌套了命令替换,以便我可以在 cron 中使用该脚本,最后在所有脚本周围添加了 Array 定义。一个已知问题是名称中包含空格的目录。然而,这已经解决了,所以为了保持简单,我认为这回答了我的问题。

