■ wc コマンド

UNIX には wc (word count) というコマンドがあります。 これと同じように、「行数」「ワード数」「バイト数」をレポートするコマンドです。

■ 使い方

引数として「ファイル」を渡すか、標準入力からファイルを読ませてください。 私が使用している ~/bin の下の結果です。

$ cd ~/bin
$ wc.rb -lwc *.rb
      483     1332    12582 c2html.rb
      350      891     8425 c2t.rb
      263      745     5661 cal.rb
       57      116      912 cat.rb
       32       77      537 date.rb
       17       40      363 deltag.rb
      156      509     3785 df.rb
      121      259     1982 dircmp.rb
      105      237     1759 ds2.rb
       63      132     1025 ds2d.rb
       49      142      974 du.rb
       57      145     1042 env.rb
       81      216     1404 esc.rb
       36       76      533 fold.rb
       32       92      636 from.rb
       62      160     1194 ftime.rb
       30       80      672 getconf.rb
      156      412     3203 hd.rb
       44       98      695 head.rb
      258      765     5494 html_cal.rb
       99      206     1686 html_lc.rb
       92      237     1878 html_srcinfo.rb
      289      306    13922 kaku.rb
      148      365     2582 last.rb
      325     1011     7513 ll.rb
       17       55      492 md5sum.rb
       12       41      258 mem.rb
       32       87      668 mkdir.rb
       13       45      286 mkeuc.rb
       69      178     1333 mkprof.rb
       18       50      441 mp3allinfo.rb
       24       54      440 mp3txt.rb
       17       45      362 mp3view.rb
       58      154     1177 netstat.rb
      135      349     3057 nif_cut.rb
      166      385     3752 nif_get.rb
      148      298     2549 nif_pop.rb
       67      161     1329 nif_smtp.rb
       91      202     1642 nif_sync.rb
       49      170     1052 pickup_url.rb
       85      322     2228 ppid.rb
       46      102      795 prprof.rb
      123      301     2333 pskill.rb
       20       35      288 pwget.rb
       17       40      295 re.rb
       48      118      858 rename.rb
      167      549     3975 rg.rb
       54      111      869 sdk_lc.rb
        8       27      227 sleep.rb
       98      209     1558 tail.rb
      165      392     4162 timesort.rb
       94      299     1928 tree.rb
       27       53      556 u2e.rb
       62      152      986 u22e.rb
       81      226     1589 wc.rb
       76      202     1308 who.rb
       41       97      675 wordfreq.rb
       94      280     2014 wv.rb
       48      126      784 xargs.rb
       30       87      894 zen.rb
     5705    14651   127619 total

デフォルトでは wc -l の動作をします。 オプションは次のものがあります。

オプション説明
-l行数をレポート
-wワード数をレポート
-cバイト数をレポート
-vversion をレポート

■ ソースコード

Web ブラウザでの表示上は Ruby のスクリプトですが、HTML の特殊文字をエスケープしています。 テキストとしてセーブしてご利用ください。

#! /usr/local/bin/ruby -Kn
# /home/tetsu/src/ruby/toolbox/wc.rb
# Created: July 11,2000 Tuesday 16:39:32
# Author: tetsu(WATANABE Tetsuya)
RCS_ID = '$Id: wc.rb,v 1.7 2002/04/25 19:31:19 tetsu Exp $'
# usage:

class String
  def word_size
    self.scan(/\S+/).size
  end
end

def usage
  STDERR.print "usage: #{$0} [-lwcv] [file...]\n"
  exit 1
end

opt_line = false
opt_char = false
opt_word = false

line_total = 0
char_total = 0
word_total = 0
line_count = 0
char_count = 0
word_count = 0

while ARGV[0] =~ /^-/
  $_ = ARGV.shift
  opt_char = true if ~/c/
  opt_word = true if ~/w/
  opt_line = true if ~/l/
  if ~/^-v/
    print RCS_ID, "\n"
    exit
  end

  usage if ~/[^-lcwv]/
end

if opt_line == false and
    opt_char == false and
    opt_word == false
  opt_line = true
end

n_arg = ARGV.size

while gets(nil)
  filename = ARGF.filename

  line_count = $_.count("\n") if opt_line
  char_count = $_.size        if opt_char
  word_count = $_.word_size   if opt_word

  printf(" %8d", line_count) if opt_line
  printf(" %8d", word_count) if opt_word
  printf(" %8d", char_count) if opt_char
  printf(" %s", filename) if filename != '-'
  puts

  line_total += line_count   if opt_line
  char_total += char_count   if opt_char
  word_total += word_count   if opt_word
end

if n_arg > 1
  printf(' %8d', line_total) if opt_line
  printf(' %8d', word_total) if opt_word
  printf(' %8d', char_total) if opt_char
  puts " total"
end

■ 解説

「ワード数」は、ちょっと数え方の工夫が必要でした。 出力を wc コマンドと合わせる必要があったので、ちょい工夫しているのですが。 漢字コードのテキストなどの場合は、正しいレポートがでない可能性があります。 この辺はさらに工夫が必要かもしれません。 日本語での語数って? どうします? Mule などと同じ数え方にしますか? (分け方というほうがいいかな)

「行数」だけ、または「バイト数」だけなら、もっと単純化できます。 ここでは、同時に複数の「数」を数えるために上記のようなコードを採用しています。 もし行数だけなら、もっと単純なコードになります。 利用価値も高いので、lc.rb というコマンドをつくってみるのもいいのではないかと思います。 私はプログラムのコメントを除いた行数を調べたくて sdk_lc.rb というコマンドをつくっていたりします。 「 Ruby でプログラムの行数を 」 を参照ください。

速度アップのために改良しました。 ある日、Ruby のソースコードを対象に動かしたら、「遅い」ということがあったのでした。

    65407   181676  1508322 total
wc.rb -lwc *.c *.h  5.90s user 0.06s system 96% cpu 6.194 total
この値は、もともとの wc.rb の速度です。 ノート PC とはいえ、Linux なのでファイルはキャッシュされています。 なのに... 遅さの原因は、「語数」の計算でした。 最初、語数を String#gsub で計算していたのです。 これで、当然置き換えが発生していて、この処理が重かったようです。
    65407   181676  1508322 total
wc.rb -lwc *.h *.c  3.97s user 0.02s system 93% cpu 4.280 total
置き換え文字列を「''」として処理するとすこし短縮しました。
    65407   181676  1508322 total
wc.rb -lwc *.h *.c  1.59s user 0.01s system 95% cpu 1.672 total
さらに? 語数を String#scan で数えたら、さらにスピードアップ。
  65407  181676 1508322 total
/usr/bin/wc *.h *.c  0.06s user 0.01s system 40% cpu 0.171 total
ちなみに普通の wc コマンドはこんなに速いんですけどね。 計測環境は Panasonic CF-B5R PentiumIII 600MHz というノート PC です。 「語数」以外は、ファイルを一度に読み込むようにしたら、速度の問題? はなくなりました。 すごく大きなファイルのことを考えて、一度に読み込むサイズ制限を加えたほうがいいかなとは思っています。

■ 履歴

1.7 2002/4/26

bug fix です。 標準入力が入力の場合、結果の出力が二回になっていました。

1.6 2002/4/26

速度アップのために大改造。

1.5 2002/3/31

ruby-1.7.2 2002-03-29 対応。 EOF 関係で、すこし動作が変わったようなので、ARGF の使いかたを見直し。


渡辺哲也(WATANABE Tetsuya): Tetsuya.WATANABE atmark nifty.com