如何在Unix中提取第三大的文件大小
在Unix系统上提取第三大文件大小的问题出现的原因是,用户有一个3.261GB的ASCII文本数据文件,每行一个键值对。该文件总共包含3,339,550,320行,并且无法在任何编辑器中打开,包括常用的Vim。用户需要对这个文件进行子集提取,以便研究一些发现,这些值只出现在大约第5,000,000,000行左右。由于文件包含如此多的行:
- 用户需要提取只有一部分行来对数据进行有意义的操作。
- 遍历到我关心的值之前的每一行将需要很长时间。
- 如果解决方案读取超过我关心的行并继续读取文件的其余部分,它将浪费时间读取近30亿个无关的行,所需时间将是必要时间的6倍。
用户最理想的解决方案是只从文件中提取一行,而不读取文件中的其他行,但用户无法想到如何在Bash中实现这一点。为了保持自己的理智,用户不会尝试读取自己问题所需的完整的5,000,000,000行。相反,用户将尝试提取3,339,550,320行中的第50,000,000行(这意味着读取整个文件将需要多出60倍的时间)。
用户使用time
内置命令对每个命令进行基准测试。
首先看看head
tail
解决方案的基准测试:
$ time head -50000000 myfile.ascii | tail -1 pgm_icnt = 0 real 1m15.321s
50,000,000行的基准测试时间为00:01:15.321,如果直接选择500,000,000行,可能需要大约12.5分钟。
接下来尝试使用cut
命令:
$ time cut -f50000000 -d$'\n' myfile.ascii pgm_icnt = 0 real 5m12.156s
这个命令运行时间为00:05:12.156,比基准测试慢得多!不确定它是否读取了整个文件或只是在第50,000,000行之前停止,但不管怎样,这似乎不是问题的可行解决方案。
接下来尝试使用AWK命令:
$ time awk 'NR == 50000000 {print; exit}' myfile.ascii pgm_icnt = 0 real 1m16.583s
这个命令运行时间为00:01:16.583,只比基准测试慢了大约1秒,但仍然没有改进。按这个速度,如果不使用exit
命令,它可能需要大约76分钟才能读取整个文件!
接下来尝试使用Perl命令:
$ time perl -wnl -e '$.== 50000000 && print && exit;' myfile.ascii pgm_icnt = 0 real 1m13.146s
这个命令运行时间为00:01:13.146,比基准测试快了大约2秒。如果在完整的500,000,000行上运行它,可能需要大约12分钟。
最后尝试使用sed命令,这是排名第一的答案:
$ time sed "50000000q;d" myfile.ascii pgm_icnt = 0 real 1m12.705s
这个命令运行时间为00:01:12.705,比基准测试快了3秒,比Perl快了约0.4秒。如果在完整的500,000,000行上运行它,可能需要大约12分钟。
总结来看,大多数情况下,很难改进head
tail
解决方案。最好的情况是,sed
解决方案提供了约3%的效率提升。
50,000,000行的结果:
1. 00:01:12.705 (-00:00:02.616 = -3.47%) sed
2. 00:01:13.146 (-00:00:02.175 = -2.89%) perl
3. 00:01:15.321 (+00:00:00.000 = +0.00%) head|tail
4. 00:01:16.583 (+00:00:01.262 = +1.68%) awk
5. 00:05:12.156 (+00:03:56.835 = +314.43%) cut
500,000,000行的结果:
1. 00:12:07.050 (-00:00:26.160) sed
2. 00:12:11.460 (-00:00:21.750) perl
3. 00:12:33.210 (+00:00:00.000) head|tail
4. 00:12:45.830 (+00:00:12.620) awk
5. 00:52:01.560 (+00:40:31.650) cut
3,338,559,320行的结果:
1. 01:20:54.599 (-00:03:05.327) sed
2. 01:21:24.045 (-00:02:25.227) perl
3. 01:23:49.273 (+00:00:00.000) head|tail
4. 01:25:13.548 (+00:02:35.735) awk
5. 05:47:23.026 (+04:24:26.246) cut
用户感到好奇,将整个文件通过cat
命令重定向到/dev/null
需要多长时间。(如果这只是一个硬盘基准测试会怎样呢?)
用户对于你拥有一个3GB以上的文本文件字典感到非常好奇。无论理由是什么,这确实非常注重文本性质。
对于单个文件,运行head
+ tail
两个进程的开销是可以忽略不计的,但当您对多个文件执行此操作时,就会开始显示出来。
在Unix中提取第三大文件大小的方法是通过使用以下命令:
ls -lS | awk 'NR==3 {print $5}'
这个问题的出现是因为用户想要找到文件系统中第三大的文件大小。解决方法是使用ls命令以及awk命令来实现。
ls命令用于列出文件和目录,-l选项表示使用长格式显示,-S选项表示按文件大小排序。接下来,使用管道将ls命令的输出传递给awk命令。
awk是一种用于处理文本文件的编程语言。在这个命令中,NR==3表示只处理第三行,{print $5}表示打印第五列,即文件大小。
通过这个命令,用户可以得到文件系统中第三大文件的大小。
这个方法的好处是简单、直接,不需要编写复杂的脚本。它只需要几个简单的命令就可以实现目标。
然而,这个方法也有一些限制。首先,它只能找到文件系统中第三大的文件,无法找到其他位置的文件。其次,它依赖于ls命令的输出格式,如果ls命令的输出格式发生变化,那么这个方法可能就无法正常工作。
通过使用ls和awk命令结合,可以方便地提取文件系统中第三大文件的大小。这个方法简单直接,适用于一般的文件大小查找任务。
问题的原因:
有一个要求从Unix中提取第三个最大的文件大小的需求。然而,已经提供的解决方法使用了head和tail命令,这对于大文件来说会很慢。因此,需要找到一种更高效的解决方法。
解决方法:
一种更高效的解决方法是使用sed命令。可以使用以下命令提取第三个最大的文件大小:
sed '3q;d' file
这个命令会打印出文件的第三行内容。
解释:
3q
会在行号为3时立即退出。
d
会删除该行而不是打印出来;在最后一行,因为q
导致脚本的剩余部分被跳过,所以不会被删除。
如果行号是一个变量,可以使用双引号而不是单引号:
sed "${NUM}q;d" file
另外,某些情况下使用tail -n+NUM file | head -n1
可能同样快或者更快。至少在我使用NUM为250000、文件有50万行的情况下,速度要快得多。你可以通过将head -n1
更改为head -nNUM2
来选择在此点之后的行数。
如果要提取从第n行到第m行的范围,可以使用以下命令:
sed -n 'n,m{p;mq;}' file
这是一个很好的解决方法,但是如果要将sed命令的输出分配给一个变量,使用命令替换可能不起作用,如果返回的行包含双星号的话。例如,如果sed "4q;d" file4
返回** banana
,那么foo=$(sed "4q;d" file4)
将把file1 file2 file3 file4 banana
分配给变量foo
。解决这个问题的方法是使用引号将命令替换起来,即foo="$(sed "4q;d" file4)"
。
最后,有人将这个解决方法制作成了一个脚本,可以从命令行接收参数,如果没有指定文件,则从标准输入中读取。脚本的使用方法是:nth line [file]
。具体的脚本可以在这里找到。
对于文件太短而没有这样一行的情况,输出将为空。
通过使用sed命令,可以更高效地提取第三个最大的文件大小,而不会导致性能下降。