使用排除列表在 bash 中查找目录

发布于 2024-12-16 00:59:34 字数 999 浏览 0 评论 0原文

现在,在您思考“以前已经这样做过”之前,请继续阅读。

像大多数尝试执行 find bash 脚本的人一样,您最终将脚本硬编码为单行命令,但最终在接下来的几个月/几年中频繁地编辑该内容,以至于您希望最终做得正确第一次。

我现在正在编写一个小备份程序来备份目录,并需要根据需要排除的目录列表找到它们。说起来容易做起来难。让我做好准备:

#!/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

现在最后你会得到 /cgi-bin,/tmp,/test,/html,/icons 这证明了这个概念是有效的,但现在要更进一步,我需要使用 find 来搜索 BasePath 并仅搜索一层深度的所有子目录并排除数组中的子目录列表。 ..

如果我手动输入它,它将是:

find /var/www/* \( -path '*/cgi-bin' -o -path '*/tmp' -o -path '*/test' -o -path '*/html' -o -path '*/icons' \) -prune -type d

我是否应该循环到每个子目录并执行相同的操作...我希望您明白我的意思。

所以我想做的事情似乎是可能的,但我有一个问题, printf ",%s" 不喜欢我使用所有这些 find -path 或 -o 选项。这是否意味着我必须再次使用 eval ?

我试图在这里使用 bash 的力量,而不是一些 for 循环。任何建设性的意见将不胜感激。

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.

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,/icons
This proves that the concept works, but now to take it a bit further I need to use find to search the BasePath and search only one level deep for all subdirectories and exclude the list of subdirectories in the array...

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?

I am trying to use the power of bash here, and not some for loop. Any constructive input would be appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你怎么敢 2024-12-23 00:59:34

尝试类似的事情

find /var/www/* \( -path "${Iggy[0]}" $(printf -- '-o -path "*%s" ' "${Iggy[@]:1}") \) -prune -type d

,看看会发生什么。

编辑:如示例中所示,将前导 * 添加到每个路径中。

这是根据您的描述提供的完整解决方案。

#!/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

$basepath 的子目录不包括 $ignore 中列出的子目录,假设 $ignore 中至少有两个(修复这个问题并不难)。

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).

少钕鈤記 2024-12-23 00:59:34

当给定的目录名称包含文字空格时,现有的答案是有问题的。安全可靠的做法是使用循环。如果您关心的是利用“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[@]}"

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. :)

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[@]}"
骄傲 2024-12-23 00:59:34
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]}

感谢@Sorpigal 我有一个解决方案。我最终嵌套了命令替换,这样我就可以在 cron 中使用脚本,最后在所有脚本周围添加了数组定义。一个已知的问题是目录名称中包含空格。然而,这个问题已经解决了,所以为了保持简单,我认为这回答了我的问题。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文