我有几个由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 中的文件。
以下 AWK 脚本应该可以解决问题,而不会使用太多内存:
它提取每个文件的路径和名称,并存储每个名称看到的最后一个路径。如果它以前看到过另一个路径,它会输出名称,除非它已经输出了它。
您的代码的主要问题是您正在收集变量中的所有路径名,然后循环它以调用
basename
. 这使它变慢。该循环还运行在未加引号的变量 expand 上
${PATHLIST}
,如果路径名包含空格或 shell 通配符,这将是不明智的。在bash
(或其他支持它的 shell)中,人们会使用数组来代替。建议:
第一个
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
条目的替代方法:谢谢你们俩的回答,也感谢艾萨克的评论。
我已经获取了您所有的代码并将它们放入脚本中
stephen.awk
kusa.sh
,isaac.sh
然后我运行了一个像这样的小基准测试:使用命令
time
我比较它们,结果如下:斯蒂芬.awk
stephen.awk : 在第二个块之前用 /IN_OPEN/ 更新
kusa.sh
使用过滤器更新
IN_OPEN
:旁注:
虽然正确,但我输出了很多空白行
sed
,但您的脚本是唯一这样的脚本。艾萨克.sh
开启过滤器
IN_OPEN
:我的脚本
@Stephen 你显然赢得了这个,令人印象深刻的时间减少了 2.5 倍。
尽管在考虑了更多之后我想到了另一个想法,但如果我只查看 OPEN 文件事件会降低复杂性,并且您不应该在不先打开文件的情况下访问或写入文件,所以我这样做了:
有了这个唯一的修改,它给了我相同的结果,我最终得到了这个
time
值:而且我很确定还有很多其他的事情我可以做