AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 437052
Accepted
Kiwy
Kiwy
Asked: 2018-04-12 07:28:57 +0800 CST2018-04-12 07:28:57 +0800 CST 2018-04-12 07:28:57 +0800 CST

脚本优化以在巨大的 CSV 中查找重复文件名

  • 772

我有几个由inotify脚本生成的 1MB 到 6GB 的 CSV 文件,其中的事件列表格式如下:
timestamp;fullpath;event;size.

这些文件的格式如下:

timestamp;fullpath;event;size
1521540649.02;/home/workdir/ScienceXMLIn/config.cfg;IN_OPEN;2324
1521540649.02;/home/workdir/ScienceXMLIn/config.cfg;IN_ACCESS;2324
1521540649.02;/home/workdir/ScienceXMLIn/config.cfg;IN_CLOSE_NOWRITE;2324
1521540649.02;/home/workdir/quad_list_14.json;IN_OPEN;2160
1521540649.03;/home/workdir/quad_list_14.json;IN_ACCESS;2160
1521540649.03;/home/workdir/quad_list_14.json;IN_CLOSE_NOWRITE;2160
1521540649.03;/home/workdir/ScienceXMLIn/masterbias_list.asc;IN_OPEN;70
1521540649.03;/home/workdir/ScienceXMLIn/masterbias_list.asc.1;IN_OPEN;80
1521540649.03;/home/workdir/ScienceXMLIn/masterbias_list.asc.2;IN_OPEN;70
1521540649.03;/home/workdir/otherfolder/quad_list_14.json;IN_OPEN;2160
1521540649.03;/home/workdir/otherfolder/quad_list_14.json;IN_CLOSE_NOWRITE;2160

我的目标是识别出现在不同文件夹中的同名文件。
在此示例中,该文件quad_list_14.json同时出现在/home/workdir/otherfolder和/home/workdir/中。

我想要的输出很简单,只是出现在多个文件夹中的文件列表,在这种情况下,它看起来像这样:

quad_list_14.json

为此,我编写了一小段代码:

#this line cut the file to only get unique filepath
PATHLIST=$(cut -d';' -f 2 ${1} | sort -u)
FILENAMELIST=""

#this loop build a list of basename from the list of filepath
for path in ${PATHLIST}
do
    FILENAMELIST="$(basename "${path}")
${FILENAMELIST}"
done

#once the list is build, I simply find the duplicates with uniq -d as the list is already sorted
echo "${FILENAMELIST}" | sort | uniq -d

不要在家里使用这个代码,这很糟糕,我应该用这样的在线人替换这个脚本:

#this get all file path, sort them and only keep unique entry then
#remove the path to get the basename of the file 
#and finally sort and output duplicates entry.
cut -d';' -f 2 ${1} | sort -u |  grep -o '[^/]*$' | sort | uniq -d

我的问题虽然仍然存在,而且文件很多,最短需要 0.5 秒,但最长需要 45 秒在 SSD 上(而且我的生产磁盘不会那么快)在不同的文件夹中找到重复的文件名。

我需要改进此代码以使其更高效。我唯一的限制是我无法完全加载 RAM 中的文件。

bash awk
  • 3 3 个回答
  • 106 Views

3 个回答

  • Voted
  1. Best Answer
    Stephen Kitt
    2018-04-12T07:39:20+08:002018-04-12T07:39:20+08:00

    以下 AWK 脚本应该可以解决问题,而不会使用太多内存:

    #!/usr/bin/awk -f
    
    BEGIN {
        FS = ";"
    }
    
    {
        idx = match($2, "/[^/]+$")
        if (idx > 0) {
            path = substr($2, 1, idx)
            name = substr($2, idx + 1)
            if (paths[name] && paths[name] != path && !output[name]) {
                print name
                output[name] = 1
            }
            paths[name] = path
        }
    }
    

    它提取每个文件的路径和名称,并存储每个名称看到的最后一个路径。如果它以前看到过另一个路径,它会输出名称,除非它已经输出了它。

    • 3
  2. Kusalananda
    2018-04-12T07:51:44+08:002018-04-12T07:51:44+08:00

    您的代码的主要问题是您正在收集变量中的所有路径名,然后循环它以调用basename. 这使它变慢。

    该循环还运行在未加引号的变量 expand 上${PATHLIST},如果路径名包含空格或 shell 通配符,这将是不明智的。在bash(或其他支持它的 shell)中,人们会使用数组来代替。

    建议:

    $ sed -e '1d' -e 's/^[^;]*;//' -e 's/;.*//' file.csv | sort -u | sed 's#.*/##' | sort | uniq -d
    quad_list_14.json
    

    第一个sed选择路径名(并丢弃标题行)。这也可以写为awk -F';' 'NR > 1 { print $2 }' file.csv或tail -n +2 file.csv | cut -d ';' -f 2。

    为我们sort -u提供了唯一的路径名,以下sed为我们提供了基本名称。最后sort的带有uniq -d告诉我们哪些基本名称是重复的。

    最后sed 's#.*/##'一个为您提供基本名称的参数让人想起${pathname##*/}等效于$( basename "$pathname" ). 它只是删除所有内容,包括/字符串中的最后一个。

    与您的代码的主要区别在于,不是使用basename多次调用的循环,而是使用单个sed来从路径名列表中生成基本名称。


    仅查看IN_OPEN条目的替代方法:

    sed -e '/;IN_OPEN;/!d' -e 's/^[^;]*;//' -e 's/;.*//' file.csv | sort -u | sed 's#.*/##' | sort | uniq -d
    
    • 3
  3. Kiwy
    2018-04-13T00:42:22+08:002018-04-13T00:42:22+08:00

    谢谢你们俩的回答,也感谢艾萨克的评论。
    我已经获取了您所有的代码并将它们放入脚本中stephen.awk kusa.sh,isaac.sh然后我运行了一个像这样的小基准测试:

    for i in $(ls *.csv)
    do
        script.sh $1
    done
    

    使用命令time我比较它们,结果如下:

    斯蒂芬.awk

    real    2m35,049s
    user    2m26,278s
    sys     0m8,495s
    

    stephen.awk : 在第二个块之前用 /IN_OPEN/ 更新

    real    0m35,749s
    user    0m15,711s
    sys     0m4,915s
    

    kusa.sh

    real    8m55,754s
    user    8m48,924s
    sys     0m21,307s
    

    使用过滤器更新IN_OPEN:

    real    0m37,463s
    user    0m9,340s
    sys     0m4,778s
    

    旁注:
    虽然正确,但我输出了很多空白行sed,但您的脚本是唯一这样的脚本。

    艾萨克.sh

    grep -oP '^[^;]*;\K[^;]*' file.csv | sort -u | grep -oP '.*/\K.*' | sort | uniq -d
    real    7m2,715s
    user    6m56,009s
    sys     0m18,385s
    

    开启过滤器IN_OPEN:

    real    0m32,785s
    user    0m8,775s
    sys     0m4,202s
    

    我的脚本

    real    6m27,645s
    user    6m13,742s
    sys     0m20,570s
    

    @Stephen 你显然赢得了这个,令人印象深刻的时间减少了 2.5 倍。

    尽管在考虑了更多之后我想到了另一个想法,但如果我只查看 OPEN 文件事件会降低复杂性,并且您不应该在不先打开文件的情况下访问或写入文件,所以我这样做了:

    #see I add grep "IN_OPEN" to reduce complexity
    PATHLIST=$(grep "IN_OPEN" "${1}" | cut -d';' -f 2 | sort -u)
    FILENAMELIST=""
    for path in ${PATHLIST}
    do
        FILENAMELIST="$(basename "${path}")
    ${FILENAMELIST}"
    done
    echo "${FILENAMELIST}" | sort | uniq -d
    

    有了这个唯一的修改,它给了我相同的结果,我最终得到了这个time值:

    real    0m56,412s
    user    0m27,439s
    sys     0m9,928s
    

    而且我很确定还有很多其他的事情我可以做

    • 1

相关问题

  • 多行文件洗牌

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve