获取运行命令时修改的文件列表的有效方法是啥?

Posted

技术标签:

【中文标题】获取运行命令时修改的文件列表的有效方法是啥?【英文标题】:What's an efficient way to get a list of the files which were modified when a command was run?获取运行命令时修改的文件列表的有效方法是什么? 【发布时间】:2019-07-23 21:19:29 【问题描述】:

我的团队有一个程序,它在运行时会生成大量临时文件,并在完成后将其删除。不幸的是,如果程序被中断,这意味着这些文件会留在程序目录树中的任意位置(通常与创建文件的各个脚本一起)。

为了简化这些情况的清理工作,我们希望重构代码以将所有临时文件放在一个指定的目录中。

第一步似乎是获取我们正在生成的所有临时文件的列表。我已经成功地做到了这一点:

    打开 BASH 外壳 cd到程序目录 运行inotifywait -m --timefmt "%F %T" --format "%T %w %f %e" -r . >> modified_files.log 打开另一个 BASH shell 在新的 shell 中运行程序 等待几个小时让程序完成运行

    在第一个 shell 中终止 inotifywait 进程。 modified_files.log 现在将包含数百万行(数百兆字节)的输出,如下所示:

    2019-07-23 12:28:33 ./project/some_dir/ some_file OPEN
    2019-07-23 12:28:33 ./project/some_dir/ some_file MODIFY
    2019-07-23 12:28:33 ./project/some_dir/ some_file CLOSE_WRITE,CLOSE
    2019-07-23 12:28:33 ./project/some_other_dir/ some_other_file OPEN
    2019-07-23 12:28:33 ./project/some_other_dir/ some_other_file MODIFY
    2019-07-23 12:28:33 ./project/some_other_dir/ some_other_file CLOSE_WRITE,CLOSE
    

    modified_files.log 传递给以下脚本:

    #!/bin/bash -e
    
    # We'll store paths to the modified files here without any duplicates
    declare -A UNIQUE_FILES
    
    # Iterate over every line of output in modified_files.log
    while IFS= read -r line; do
    
        # In the first line from the output example this would find ./project/some_dir/
        directory="$(grep -Po ".*?\s.*?\s\K.*?(?=\s.*)" <<< "$line")"
    
        # In the first line from the output example this would find some_file
        file="$(grep -Po ".*?\s.*?\s.*?\s\K.*?(?=\s.*)" <<< "$line")"
    
        path="$directory$file"
    
        # Only record the path from this output line if we haven't already recorded it
        if [[ -n "$path" ]] && [[ -z "$UNIQUE_FILES["$path"]" ]]; then
            UNIQUE_FILES["$path"]=1
        fi
    done < "$1"
    
    # Save all of the recorded paths as separate lines within a single 'list' variable
    for unique_file in "$!UNIQUE_FILES[@]"; do
        list="$list"$'\n'"$unique_file"
    done
    
    # Sort the 'list' variable to make the list of paths visually easier to read
    list="$(echo "$list" | sort)"
    
    # Print the paths of all the modified files
    echo "$list"
    

这可行,但解析 inotifywait 产生的每一兆字节的输出大约需要一分钟。我觉得下次需要时应该有一种更快的方法来做到这一点。我希望解决方案可以解决以下任一问题:

上面显示的 grep 命令效率低下(IE:可能使用对 sed/awk 的调用来代替?) 解析脚本通常效率低下 我正在使用的 inotifywait 命令效率低下(即:删除时间戳或使用一些特殊标志调用它以减少冗长) 上面列出的一般流程效率低下

【问题讨论】:

我建议查看EXIT 上的清理陷阱,例如:redsymbol.net/articles/bash-exit-traps touch ./canary; run-the-programm。然后find ./ -anewer canari -delete 您依赖的文件名不包含空格;这应该与您的脚本具有相同的输出:awk 'print $3 $4' modified_files.log | sort -u @AlexJohnson, ...btw,作为一个注释 - 您将获得数量级的性能增强,只需在内循环中使用本机 bash 字符串操作,而 不是(永远!)运行命令替换、管道或其他涉及对每行输入读取进行分叉的操作。 (运行一个处理 10,000 行的 grep 很好;运行 10,000 个单独的 grep 副本(每行一个)根本不好)。幸运的是,bash 具有非常强大的字符串操作原语,因此您在此代码中不需要任何grep 位。 将您的正则表达式从 PCRE 重写为 POSIX ERE,[[ $string =~ $re ]] 将为您提供非常快速的进程内匹配,将生成的匹配组放入 BASH_REMATCH 数组中。 【参考方案1】:

strace 可能有效,尽管it can cause performance issues。

您会查找已打开用于写入的文件,或者您可以只检查已删除/取消链接的文件(参见System calls in Linux that can be used to delete files)

strace 输出中的文件名可能是相对于当前目录给出的,因此您可能也需要记录 chdir()。

基本调用是:

strace -f -o LOGFILE -e WhatToTrace -- PROGRAM ARGUMENTS

WhatToTrace 中包含的系统调用示例如下:

openat,open,creat - 跟踪文件访问/创建 mkdirat,mkdir - 跟踪目录创建 unlinkat,unlink,rmdir - 查找已删除的文件和目录 chdir - 当前工作目录更改时记录 renameat,rename - 查找被覆盖的文件

获得LOGFILE后,您可以编写一个简单的脚本来处理已记录的路径。

【讨论】:

如果你想要的东西比 strace 快得多,,看看 sysdig 和其他客户端到 eBPF 子系统。 strace 以 500% 的开销跟踪单个进程(如果你幸运的话!),sysdig 以 3% 的开销(将事件转储到环形缓冲区)一次跟踪整个操作系统被洗牌到用户空间以供以后过滤/分析)。 (sysdig 不只是一个 eBPF 客户端,但是他们的 eBPF 版本的探针在最近的内核中更可靠——尽管据报道他们今天刚刚找到了一个解决方案,它还没有合并,更不用说发布了)。 @CharlesDuffy sysdig可以作为没有root的普通用户来加载内核模块或者其他特殊权限吗? 需要系统管理员合作。 (因为它不仅限于跟踪一个用户的进程,否则这将是一个重大的安全漏洞;任何人都可以窥探其他人的进程、数据输入等)。也就是说,有一种可用的过滤器语言以及对更高级过滤器的 lua 支持,因此系统管理员可以轻松地将仅包含一个用户事件的跟踪流式传输到用户可以读取的位置...... 记录一下:使用 strace 运行我的程序比不使用 strace 的时间长 27%。在影响更大的情况下,sysdig 看起来像是一个很好的工具。

以上是关于获取运行命令时修改的文件列表的有效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

path是啥意思

linux中Shell历史命令记录文件的路径是啥

windows中type命令的源码是啥

Cassandra,运行子查询的有效方法是啥

数据库 之 参数修改

linux加载配置文件命令是啥