需要帮助运行 find 命令从 for 循环中读取文件名

需要帮助运行 find 命令从 for 循环中读取文件名

的内容/tmp/fefile

amx/eng/prf.amx
amx/eng/det.amx
bmb/menu.bmb
bmx/eng/menu.bmx
dll/tlnt.dll
dlx/eng/dlx
for file in `cat /tmp/fefile`
do
    if [ -f $file ]
    then
        echo "File '${file}' found in $(pwd) path."
        echo " Now i need to check if the  that file modified in last 10 mins with the below find command "
        find . -mmin -10 -type f -name ${file} -regextype posix-egrep -regex ".*/(dir1|dir1|dir3|dir4)/.+" -printf "%P\n" > /tmp/base
        echo "The files that are modified recently are below"
        File=(cat /tmp/base)

        echo " now i am verifying that $file is matched with $File "
        if[ $file == $File ]
        then
            echo " tmp file matched with base file."
        else
            echo " file doesn't match"
        fi # Originally missing
    else
        echo "File '{file} not found."
    fi
done

请帮助我更正 find 命令中的上述脚本,以读取文件名并检查它是否在最近 10 分钟内被修改

如果该文件被修改并检查两个文件是否匹配

答案1

在里面寻找命令,您使用正则表达式。该正则表达式查看列出的文件名菲菲莱文件,但没有一个与此正则表达式匹配。注意:它查看文件名本身,而不是文件内部

find . -mmin -10 -type f -name ${file} -regextype posix-egrep -regex ".*/(dir1|dir1|dir3|dir4)/.+" -printf "%P\n" > /tmp/base

他们都没有:

  • anything in any amount
  • dir1dir1(再次?!也许是dir2)或 dir3dir4
  • anything in any amount but at least one

另一个问题是正则表达式本身,它是:

".*/(dir1|dir1|dir3|dir4)/.+"

也许应该是:

".*\/(dir1|dir2|dir3|dir4)\/.+"

您应该使用 转义/\如:\/

还:

File=(cat /tmp/base)

应该:

File=$(cat /tmp/base)

或者

File=`cat /tmp/base`

另一点是行尾find

(...) -printf "%P\n" > /tmp/base

最好将 > 更改为 >>:

(...) -printf "%P\n" >> /tmp/base

否则,它将覆盖找到的所有文件,只保留一个。

答案2

脚本片段的主要问题是您在循环中多次运行 find (对于 中的每个文件名运行一次/tmp/fefile)。

这是非常缓慢和低效的,find因为“昂贵的”操作(使用任何工具递归目录树在时间和磁盘 I/O 方面都是昂贵的),而不是您应该在循环中重复运行的操作,除非您别无选择(而且几乎总是有另一个更好的选择)。

最好find只运行一次并处理其输出(例如使用 grep 或 awk 或 sed 或其他)。

尝试更多类似这样的事情:

find ./dir[1234]/ -type f -mmin -10 -printf '%P\n' | grep -F -f /tmp/fefile

这将输出 dir1..dir4 中所有文件的列表,这些文件 a) 在过去 10 分钟内被修改,b) 与/tmp/fefile.

顺便说一句,请注意,这不需要/tmp/base临时文件(顺便说一句,将临时文件名称硬编码到脚本中通常是一个坏主意,使用mktemp或类似的代替。我猜你/tmp/fefile几乎肯定不应该被硬编码,但是我不知道你的脚本的其余部分做了什么或者这个脚本片段是如何执行的)

你可能需要稍微调整find和/或grep选项才能得到你想要的——我花了几分钟检查你的脚本片段来弄清楚你想要做什么,但我仍然不是 100%当然。我确实知道您正在使用大约 20 行 shell 代码来非常低效地完成一些事情,而您可以单独使用 find 或使用 find 和 grep (或其他一些常用工具,例如 sed 或 awk 或 perl)来做得更好更快。

注意:如果任何文件名包含换行符,这将无法正常工作。您可以在格式字符串中使用\0not以及 GNU grep 的选项。\n-printf-z

find ./dir[1234]/ -type f -mmin -10 -printf '%P\0' | grep -z -F -f /tmp/fefile

(要在终端中查看输出,您可能需要将 NUL 分隔符转换为换行符,例如通过将输出传输到tr '\0' '\n'。这对于仅显示文件名列表来说很好,但如果您需要对文件名执行某些操作,则不安全)

而且,说到使用文件名进行操作,最好、最安全的方法之一是将它们存储在数组中。例如,通过使用 bash 内置mapfile(AKA readarray)以及流程替代用所有匹配的文件名填充数组。

declare -a found
mapfile -d '' -t found < <(find ./dir[1234]/ -type f -mmin -10 -printf '%P\0' |
                             grep -z -F -f /tmp/fefile)

$found将是一个包含所有匹配文件名的数组。您可以使用查看数组declare -p found(这对于调试目的最有用,以验证数组是否包含您认为应该包含的内容)或将其用作命令的参数,或在循环中使用,例如:

for f in "${found[@]}"; do
  echo "$f"
done

"$f"您可以在循环中执行任何您想要执行的操作,但请记住对变量和数组使用双引号,因为它们可以包含除 NUL 之外的任何字符。

这提醒我,您${file}find命令中使用,而不是"$file".这是一个非常常见的错误:变量周围的花括号是不是引用的替代品。

它们用于参数替换(运行man bash并搜索Parameter Expansion标题)和在字符串中插入变量名称时消除歧义(例如,当您调用一个变量$foo并且需要将其打印在紧邻有效变量名字符的字符串中时 -echo "$food"将输出 $food 的值,而echo "${foo}d"将输出 $foo 的值,后跟一个文字d字符)。

$VAR 与 ${VAR} 以及引用或不引用

也可以看看为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?,什么时候需要双引号?忘记在 bash/POSIX shell 中引用变量的安全隐患

最后,由于这个问题是关于find并处理其输出的,并且因为您一直在问几个与查找相关的问题,请参阅为什么循环查找的输出是不好的做法?。并且不要忘记阅读它链接到的相关问题。

答案3

如果重点是查找最近 10 分钟内最后修改的常规文件,其最后路径组件是组成 行的任何字符串/tmp/fefile,并且其路径至少包含dir1, dir2, dir3, dir4, 列表中的一个目录组件,你不能通过 进行匹配-name,必须在完整的 上完成-path

-path(最初来自 BSD,但现在是标准),并且通过一些实现-wholename(与 相同-path),-ipath, -regex,-iregex匹配整个路径。

所以一些选项是

  • 生成一个命令行,该命令行对 的每一行find使用谓词:-path/tmp/fefile

    LC_ALL=C find . '(' -path '*/dir1/*' -o \
                        -path '*/dir2/*' -o \
                        -path '*/dir3/*' -o \
                        -path '*/dir4/*' \
                    ')' '(' \
                        -path '*/amx/eng/prf.amx' -o \
                        -path '*/amx/eng/det.amx' -o \
                        ... \
                    ')' -type f -mmin -10
    

    使用 bash 你可以这样做:

    readarray -t args < <(
      </tmp/fefile LC_ALL=C sed '
    1!i\
    -o
    i\
    -path
    s|[*/?\\]|\\&|g; # escape glob operators
    s|.*|*/&|')
    LC_ALL=C find . '(' -path '*/dir1/*' -o \
                        -path '*/dir2/*' -o \
                        -path '*/dir3/*' -o \
                        -path '*/dir4/*' \
                    ')' '(' "${args[@]}" ')'
    

    (或者*/dir[1234]/*,我假设这些是一些真实目录名称的占位符,这些名称无法轻松地以一种模式分解)。

  • 将路径匹配留给后处理命令如@cas所示

  • 或者因为这里你的find实现似乎是find支持-regex谓词的 GNU,所以动态构造正则表达式:

    regex=".*/($(
      </tmp/fefile LC_ALL=C sed -e 's/[][$^*()+{}\\|.?]/\\&/g' |
      paste -sd '|' -))\$"
    LC_ALL=C find . -regextype posix-extended \
                    -regex '.*/(dir1|dir2|dir3|dir4)/.*' \
                    -regex "$regex" \
                    -type f -mmin -10
    

    (假设/tmp/fefile不为空)。

-exec如果您需要在这些文件上运行命令,请添加一些谓词,或者-print0(或-printf '%P\0'删除前导./)将 NUL 分隔的列表传递给可以处理 NUL 分隔列表的其他命令(一种传递任意文件列表的安全方法)路径)。

相关内容