Linux などで使われている md5sum コマンドです。 MD5 を利用したチェックサムを確認するコマンドです。
ファイルのチェックサムを確認します。 引数に確認したいファイル名を渡した場合は次のような結果になります。
$ ll *.rb -rw-r--r-- 1 tetsu staff 2677 May 3 2001 instruby.rb -rw-r--r-- 1 tetsu staff 3487 Mar 21 04:48 mkconfig.rb -rw-r--r-- 1 tetsu staff 978 May 8 2001 rubytest.rb $ md5sum.rb *.rb 3f38e2d8ac1efbe5e4b530e2ecd8feb9 instruby.rb a726fa8939baddd0cdb227480d387edc mkconfig.rb 82336e066b58c56c368c3490ccff11f9 rubytest.rb
一度チェックサムを計算しておくと、あとで変更されているか確認可能です。 Ruby のバイナリで確認してみます。 2002/5/1 のバイナリと 2002/5/4 のバイナリの比較です。
$ pwd /t/src/ruby/1.7/2002/05/01 $ ll ruby-1.7.2 libruby-1.7.2.a -rw-r--r-- 1 tetsu staff 912262 May 1 22:48 libruby-1.7.2.a -rwxr-xr-x 1 tetsu staff 585488 May 1 22:49 ruby-1.7.2* $ md5sum.rb ruby-1.7.2 libruby-1.7.2.a > /tmp/j.txt $ md5sum.rb -c /tmp/j.txt ruby-1.7.2: OK libruby-1.7.2.a: OK $ cd ../04 $ pwd /t/src/ruby/1.7/2002/05/04 $ ll ruby-1.7.2 libruby-1.7.2.a -rw-r--r-- 1 tetsu staff 911966 May 5 02:17 libruby-1.7.2.a -rwxr-xr-x 1 tetsu staff 585296 May 5 02:18 ruby-1.7.2* $ md5sum.rb -c /tmp/j.txt ruby-1.7.2: FAILED libruby-1.7.2.a: FAILED /home/tetsu/bin/md5sum.rb: WARNING: 2 of 2 computed checksum did NOT match zsh: 5180 exit 1 md5sum.rb -c /tmp/j.txt $ md5sum.rb -c --status /tmp/j.txt zsh: 5185 exit 1 md5sum.rb -c --status /tmp/j.txt
-c オプションを指定した場合、チェックサムが一致しない場合は、ファイル単位にレポートします。
また、同時に --status オプションをしていすると、終了値だけになります。
ここで使用しているシェルは zsh です。
zsh は、終了ステータスをその場で確認可能です。
他のシェルを使っている場合は、終了ステータスは明示的に確認しないとわからないので注意ください(シェル変数 $?, $status)。
オプション 意味 -c, --checkチェックサムの結果からファイルを確認します --statusチェックサム確認のステータスだけ返します --checkオプションと合わせて使用します
Web ブラウザでの表示上は Ruby のスクリプトですが、HTML の特殊文字をエスケープしています。 テキストとしてセーブしてご利用ください。
#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/toolbox/md5sum.rb
# Created: January 28,1999 Thursday 12:42:33
# Author: tetsu(WATANABE Tetsuya)
# $Id: md5sum.rb,v 1.4 2007/05/07 06:29:14 tetsu Exp $
# usage:
# hint: ruby-list 11775 11780
require 'md5'
def md5sum(str)
md5 = MD5.new
md5.update(str)
md5.hexdigest
end
# close したいだけ close しなくてもいいかな
# File.open(filename).read で代用化
def fileread(file)
f = File.open(file)
str = f.read
f.close
str
end
def usage
STDERR.puts "usage: #{$0} [OPTION] [FILE]...
-c, --check check MD5 sums against given list
--status do not output anything, status code shows success"
exit 1
end
opt_check = false
opt_status = false
while ARGV[0] =~ /^-/
$_ = ARGV.shift
if ~/^-c/ or ~/^--check/
opt_check = true
elsif ~/^--status/
opt_status = true
else
usage
end
end
if opt_status == true and opt_check == false
STDERR.puts "#{$0}: the --status option is meaningful only when verifying checksums(--check)"
exit 1
end
if opt_check
ck_count_total = 0
ck_count_failed = 0
end
while file_str = gets(nil)
if opt_check
file_str.split("\n").each do |l|
ck_count_total += 1
sum1, fname = l.split
sum2 = md5sum(fileread(fname))
status = if sum1 == sum2
'OK'
else
ck_count_failed += 1
'FAILED'
end
if opt_status
else
puts fname + ': ' + status
end
end
else
puts md5sum(file_str) + ' ' + ARGF.filename
end
end
if opt_check and ck_count_failed > 0
if opt_status
else
STDERR.puts "#{$0}: WARNING: #{ck_count_failed} of #{ck_count_total} computed checksum did NOT match"
end
exit 1
end
今回、対象となる「ファイル名」をつぎつぎ引数として処理を行なうのではなく、ARGF を利用しています。 これは、引数にファイルがなく標準入力が対象の場合に、記述する処理を共通化するのが楽だからです。 最初は、「ファイル名」を ARGV からとりだして、処理していました。 ファイルが対象の場合と標準入力が対象の場合に、どちらも共通の記述をうまく書くことができるので、私はこういう書き方がけっこう好きです。 標準入力とファイルの処理を明示的に記述すると、こういう感じでしょうか。
require 'md5'
def md5sum(f)
md5 = MD5.new
while mb = f.read(1024 * 1024)
md5.update(mb)
end
md5.hexdigest
end
if ARGV.size == 0
puts md5sum(STDIN) + ' -'
else
while filename = ARGV.shift
f = File.open(filename)
puts md5sum(f) + ' ' + filename
f.close
end
end
--check オプションがないと、けっこう素直に書けるんですが?
処理をサブルーチンに分ける書きかたを基本にして書いてみました。
どちらのほうがわかりやすいですか?
メインの処理の流れについては、こちらのほうがわかりやすいですね。
それと、ファイルを 1MB 単位に読んでいるので、サイズがめちゃくちゃ大きなファイルの場合、こちらのほうがいいでしょう。
#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/toolbox/md5sum2.rb
# Created: May 06,2002 Monday 12:40:54
# Author: tetsu(WATANABE Tetsuya)
# $Id: md5sum2.rb,v 1.1 2002/05/06 03:41:13 tetsu Exp $
# usage:
require 'md5'
def check(f, opt_status)
ck_count_total = 0
ck_count_failed = 0
while l = f.gets
ck_count_total += 1
sum1, filename = l.split
r = File.open(filename)
sum2 = md5sum(r)
r.close
status = if sum1 == sum2
'OK'
else
ck_count_failed += 1
'FAILED'
end
if opt_status
else
puts filename + ': ' + status
end
end
if ck_count_failed > 0
if opt_status
else
STDERR.puts "#{$0}: WARNING: #{ck_count_failed} of #{ck_count_total} computed checksum did NOT match"
end
return 1
end
0
end
def md5sum(f)
md5 = MD5.new
while mb = f.read(1024 * 1024)
md5.update(mb)
end
md5.hexdigest
end
def usage
STDERR.puts "usage: #{$0} [OPTION] [FILE]...
-c, --check check MD5 sums against given list
--status do not output anything, status code shows success"
exit 1
end
opt_check = false
opt_status = false
while ARGV[0] =~ /^-/
$_ = ARGV.shift
if ~/^-c/ or ~/^--check/
opt_check = true
elsif ~/^--status/
opt_status = true
else
usage
end
end
if opt_status == true and opt_check == false
STDERR.puts "#{$0}: the --status option is meaningful only when verifying checksums(--check)"
exit 1
end
status = 0
if ARGV.size == 0
if opt_check
status = check(STDIN, opt_status)
else
puts md5sum(STDIN) + ' -'
end
else
while filename = ARGV.shift
f = File.open(filename)
if opt_check
status = check(f, opt_status)
else
puts md5sum(f) + ' ' + filename
end
f.close
end
end
exit status
やっぱり、こちらのほうがいいかも?
メイン処理が読みやすいということは、流れがわかりやすいので大切ですしね。
とりあえずは、こういう書きかたがあるという比較サンプルとして使ってください。
一度チェックサムを計算した結果が入ったファイルから、ファイルの確認を行なう --check オプションの場合、サブルーチンにすることも考えました。
今回は、記述量がすくなかったので、そのまま書いてしまいましたが、明示的に分けてしまうのも処理の流れを読みやすくするという意味でいいと思います。
MD5 のチェックサム、以前 ruby-list 11775 11780 で話題になったのですが、一行で書くとこんな感じ。
$ ruby -r md5 -e 'puts MD5.new(open("ruby-1.7.2").read).hexdigest'
b9acecbcd93cbceb5104d8ae7ef6a873
$ md5sum.rb ruby-1.7.2
b9acecbcd93cbceb5104d8ae7ef6a873 ruby-1.7.2
システム管理的な作業としては、チェックサムをとっておくと、あとあと便利かも。
システムには、いろいろな方法で、チェックサムを検証するツールがあったりすると思います。 セキュリティが心配される昨今ですので、自分なりに確認する方法を持つといいかもしれませんね。# find /sbin /usr/sbin /etc /usr/bin /usr/lib -type f | xargs md5sum.rb > md5.txt
MD5 の扱いでエラーになったので対応。
usage の修正。
最初の公開です。