2019年5月6日月曜日

シェルスクリプトでファイルサイズを取得するベストな方法は?

シェルスクリプトでファイルサイズを取得するのに、今まで次のように書いていて、常套句と思っていました。
[root@hoge tmp]# uname -a
Linux hoge 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
[root@hoge tmp]# ls -l /etc/services 
-rw-r--r-- 1 root root 670293 Jun  7  2013 /etc/services
[root@hoge tmp]# declare -i fsize=`wc -c /etc/services`
bash: declare: 670293 /etc/services: division by 0 (error token is "/services")
[root@hoge tmp]# echo $fsize

[root@hoge tmp]# wc -c /etc/services 
670293 /etc/services
[root@hoge tmp]# declare -i fsize=`wc -c /etc/services | gawk '{print $1}'`
[root@hoge tmp]# echo $fsize
670293
gawk まで呼び出すのがイマイチだなとは思っていたのですが、本日ふと思い立ち、調べてみたら先人の方が居られました。
https://ameblo.jp/archive-redo-blog/entry-10196055325.html
なるほど標準入力にリダイレクトすれば1コマンドで済むのですね。賢い方法と思いました。
[root@hoge tmp]# declare -i fsize=`wc -c < /etc/services`                    
[root@hoge tmp]# echo $fsize
670293
これからは、このパターンを使おうと思います。
ところで、wc というコマンド名からは、ファイルを全部読んでファイルサイズを得る動きをしそうに見えますが、そこは賢い実装になっており、-c オプション指定だと fstat(2) システムコールが利用され、ファイルを全部読み出すなどという非効率な振舞いにはならないようです。
[root@hoge tmp]# strace wc -c < /etc/services
execve("/usr/bin/wc", ["wc", "-c"], [/* 46 vars */]) = 0
brk(NULL)                               = 0x1e76000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa4cf32f000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=90399, ...}) = 0
mmap(NULL, 90399, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa4cf318000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340$\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2151672, ...}) = 0
mmap(NULL, 3981792, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa4ced42000
mprotect(0x7fa4cef04000, 2097152, PROT_NONE) = 0
mmap(0x7fa4cf104000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c2000) = 0x7fa4cf104000
mmap(0x7fa4cf10a000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa4cf10a000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa4cf317000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa4cf315000
arch_prctl(ARCH_SET_FS, 0x7fa4cf315740) = 0
mprotect(0x7fa4cf104000, 16384, PROT_READ) = 0
mprotect(0x608000, 4096, PROT_READ)     = 0
mprotect(0x7fa4cf330000, 4096, PROT_READ) = 0
munmap(0x7fa4cf318000, 90399)           = 0
brk(NULL)                               = 0x1e76000
brk(0x1e97000)                          = 0x1e97000
brk(NULL)                               = 0x1e97000
fstat(0, {st_mode=S_IFREG|0644, st_size=670293, ...}) = 0
lseek(0, 0, SEEK_CUR)                   = 0
lseek(0, 0, SEEK_END)                   = 670293
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa4cf32e000
write(1, "670293\n", 7670293
)                 = 7
close(0)                                = 0
close(1)                                = 0
munmap(0x7fa4cf32e000, 4096)            = 0
close(2)                                = 0https://ameblo.jp/archive-redo-blog/entry-10196055325.html
exit_group(0)                           = ?
+++ exited with 0 +++
[root@hoge tmp]# 
ということで、これがベストな方法に思いましたが、他にあるでしょうかね?
もちろん Perl/Python/Ruby等 の汎用スクリプト言語を呼べば同様のことが出来るけど、軽量に行うには wc -c < file がベストなのではと思っています。

2019-07-24追記
stat -c %s というのもありますが、当然 -c %s を解釈する処理の分だけ遅いに違いないと思っていましたが、一応実験してみました。
[root@hoge ~]# perf stat stat -c %s /etc/services
670293

 Performance counter stats for 'stat -c %s /etc/services':

          0.728356      task-clock (msec)         #    0.749 CPUs utilized          
                 2      context-switches          #    0.003 M/sec                  
                 0      cpu-migrations            #    0.000 K/sec                  
               249      page-faults               #    0.342 M/sec                  
         2,528,856      cycles                    #    3.472 GHz                    
         1,292,306      instructions              #    0.51  insn per cycle         
           252,891      branches                  #  347.208 M/sec                  
            11,528      branch-misses             #    4.56% of all branches        

       0.000972916 seconds time elapsed

[root@hoge ~]# perf stat wc -c < /etc/services
670293

 Performance counter stats for 'wc -c':

          0.599273      task-clock (msec)         #    0.705 CPUs utilized          
                 2      context-switches          #    0.003 M/sec                  
                 0      cpu-migrations            #    0.000 K/sec                  
               219      page-faults               #    0.365 M/sec                  
         2,077,769      cycles                    #    3.467 GHz                    
         1,043,855      instructions              #    0.50  insn per cycle         
           202,792      branches                  #  338.397 M/sec                  
            10,808      branch-misses             #    5.33% of all branches        

       0.000849638 seconds time elapsed

0 件のコメント:

コメントを投稿

人気ブログランキングへ にほんブログ村 IT技術ブログへ