ここでは、いくつかの課題について、各言語でどう記述するか、書いてみようと思います。ポイントは、プログラムで処理することで、得られた情報に対して追加処理を行う余地ができることです。実現することをピックアップします。
※【更新2014/11/27】 ここで表記している「バイナリファイル」とは、lessなどでファイルを開いた時に「maybe binary file」と表示されるファイルを指しています。チェッカはrubyで記述したものを他のスクリプトでも利用するようにしています。チェック基準は、ファイルの中にasciiコード以外を含むもの、としました。現在の処理方法では、2byteコード(日本語など)を含むと、binaryファイルとしてチェックされます。コメント文中の2byteコードは無視する方がよいと思いますが、現在はその仕組みを持たせていません。
--- binChecker.rb
このRubyコードは、どこかのサイトを参考に調整を加えています。必要に応じて修正してください。
使い方:binChecker.rb filename
→ 返り値0のとき、binaryファイルと判断
【修正しました:2014/12/01】
#!/usr/bin/ruby -wdef binary?(name) if(File.size(name) > 0)then File.open(name, "rb") {|io| io.read(1024)}.each_byte do |bt| if(bt>127)then return true end end return false else return false endend##############if(binary?(ARGV[0]))then exit(0)else exit(1)end--- tcsh
処理が重いです...
コマンドプロンプトで
> ls -1R *
と書けば、ディレクトリを再帰処理で掘ってくれます。これを利用すれば簡単に実装できそうですが、ls -1R * は
sv/work/uvm-1.1d/src/tlm2:uvm_tlm2.svhuvm_tlm2_defines.svhuvm_tlm2_exports.svhこのような出力形式になります。ディレクトリ名には「:」が付加される。ファイル名にはディレクトリ名が付加させられないようなので、プログラマブルに書く必要があります。
お試しで、こんなコードを書いてみました。
#!/usr/bin/tcsh -fset filesset head = "."foreach tmp ( `ls -1R *` ) echo $tmp |grep ':$' >! /dev/null if ( $status == 0 ) then set head = `echo $tmp |sed 's/:$//'` else set file = $head/$tmp set files = ( $files $file ) endifendforeach tmp ( $files ) echo $tmpend前段のforeachで
しています。後段のforeachは結果確認用。デバッグ環境では、この時点で合計31ファイルが処理対象となっているのですが、実行の重いことといったらありません。2.5秒くらいかかりました@Core2Duo 2.2GHz Win8.1 Pro cygwin
上記コードには問題があります。
ファイル名によるフィルタリング
1.ファイル名に"abc"を含むファイル
2.拡張子を除いたファイル名に"abc"を含むファイル
foreach tmp ( $files ) set tail = $tmp:t set name = $tail:r #この文法についてはこちら if ( $name =~ *abc* ) then 処理 endifend3.拡張子が"exe"のファイル
含まれる文字列によるフィルタリング
foreach tmp ( $files ) grep keyword $tmp >! /dev/null if ( $status == 0 ) then 処理 endifendバイナリファイルチェック
このページの上の方にある、binChecker.rbを利用してある程度判定できます。ただし、ファイルを開いてチェックするため、処理が重くなります。
--- Perl
まず、カレントディレクトリ以下すべてのファイル(ディレクトリあれば掘る)を出力してみます。
#!/usr/bin/perluse File::Find;find(\&test, "./");exit;sub test { print $File::Find::name ."\n";}*1 use文で定義済みライブラリを読み込みます
*2 関数 find を呼び出します。第一引数に、処理用自作関数(ここではsub test)をリファレンス(\)指定します。第二引数に、サーチ元ディレクトリ(複数指定可)を指定します
find 関数は、ファイルを見つける毎にユーザ指定関数(ここではsub test)を呼び出すようです。
こちらのサイトを参考にしました。
globを使う場合には、ディレクトリ以下を自動サーチしてくれないので、自作で再帰処理を書く必要があります。
ファイル名によるフィルタリング
上記のsub testに対して、引数渡しでフィルタリングファイル名を指定しようとしたところ、find関数でエラーが起きました。今のところ、解決策を把握していないため、「やむを得ず」関数外の変数を利用して処理を書いてみます。
#!/usr/bin/perluse File::Find;$key = $ARGV[0]; #フィルタリングキーワードfind(\&test, "./");exit;sub test { my $fname = ""; my $ext = ""; # ファイル名(拡張子ぬき)と拡張子名を分ける if(/(\w+)\.(\w+)/){ $fname = $1; $ext = $2; }elsif(/(\w+)/){ $fname = $1; } print $File::Find::name."\n" if(/$key/); # ファイル名サーチ print $File::Find::name."\n" if($fname =~ /$key/); # ファイル名(拡張子ぬき) サーチ print $File::Find::name."\n" if($ext =~ /$key/); # 拡張子サーチ}含まれる文字列によるフィルタリング+バイナリファイルチェック
変更箇所が飛び飛びなところがあるため、フルソースで展開します。太字のところがメインソースです。バイナリチェックを有効にすると、検索ファイルが多いと処理にとても時間がかかるため、print文でドットを打って進捗が見えるようにしています。
binChecker.rbのパスを修正しました。
#!/usr/bin/perluse File::Find;$mode = 1; # ファイル名によるフィルタリングをmode=0として残しています$key = $ARGV[0]; # 含まれる文字列の指定$TOOLPATH = ""; # 上記のbinChecker.rbを置く場所find(\&test, "./");exit;sub test { #print $File::Find::name ."\n"; my $fname = ""; my $ext = ""; if($mode==0){ if(/(\w+)\.(\w+)/){ $fname = $1; $ext = $2; }elsif(/(\w+)/){ $fname = $1; } if($fname =~ /$key/){ print $File::Find::name."\n"; } }elsif($mode==1){ $fullfile = $File::Find::name; $file = $_; if(-f $file){ $binchk = system($TOOLPATH . "/binChecker.rb $file"); if($binchk==0){ print "\n*E, maybe binary file --> $fullfile\n"; }elsif( open(IN, $file) ){ @RFILE = <IN>; close(IN); @match = grep(/$key/, @RFILE); if(@match!=0){ print "\nmatch $fullfile\n"; }else{ print "."; } }else{ print "\n*E, file open error, so skip this file -> $fullfile\n"; } }else{ if(-d $file){ }elsif(-l $file){ }else{ print "\nE: Not plain file --> $fullfile\n"; } } }}print "\n";--- Ruby
まず、カレントディレクトリ以下すべてのファイル(ディレクトリあれば掘る)を出力してみます。globの中で * 指定しているので、ドットから始まるファイルは対象になりません。
#!/usr/bin/ruby -wDir::glob("**/*"){ |file| if(File::ftype(file)=="directory")then #puts "dir: " + file else puts "./#{file}" end}ファイル名によるフィルタリング:修正2014/12/01
変数 filter にフィルタリングしたい文字列を指定します。
mode==0のとき、ディレクトリ名を含めたフルパスファイル名に対してfilterをかけるときに有効にします。
mode==1のとき、ファイル名(パス除く、拡張子除く)に対してfilterをかけるときに有効にします。
mode==2のとき、拡張子に対してfilterをかけるときに有効にします。
*1 mode値取り込み時、ARGVアクセスだけだと文字列になるため、.to_i で数値化しています
#!/usr/bin/ruby -wif(ARGV.size < 2)then puts "*E, argument error" exitendmode = ARGV[0].to_ifilter = ARGV[1]puts "*** File Search, include #{filter} ***"Dir::glob("**/*"){ |file| if(File::ftype(file)=="directory")then #puts "dir: " + file elsif(File::ftype(file)=="link")then else filename = File.basename(file) if(/^([\w-]+)\.(\w+)/ =~ filename)then root = $1 ext = $2 elsif(/^(\w+)$/ =~ filename)then root = $1 ext = "" else puts "*E, Failed to get filename information... -> #{file}" next end if(mode==0 && /#{filter}/ =~ file)then puts "./#{file}" elsif(mode==1 && /#{filter}/ =~ root)then puts "./#{file}" elsif(mode==2 && /#{filter}/ =~ ext)then puts "./#{file}" end end}含まれる文字列によるフィルタリング+バイナリファイルチェック
変数 filter に、検索したい文字列を格納します(引数渡し)。
メソッド binary? を追加しています(更新2014/12/01)
#!/usr/bin/ruby -w### method binary?#########################################def binary?(name) if(File.size(name) > 0)then File.open(name, "rb") {|io| io.read(1024)}.each_byte do |bt| if(bt>127)then return true end end return false else return false endend### main process#########################################if(ARGV.size < 1)then puts "*E, argument error" exitendfilter = ARGV[0]puts "*** File Search, include #{filter} ***"Dir::glob("**/*"){ |file| if(File::ftype(file)=="directory")then #puts "dir: " + file elsif(File::ftype(file)=="link")then else flag = 0 if(binary?(file)==false)then line_cnt=0 File.open(file).each { |line| line_cnt += 1 begin if(/#{filter}/ =~ line)then flag += 1; break; end rescue puts "*E, Error detected in #{file} line=#{line_cnt}" end } puts "./#{file}" if(flag==1) else puts "*E, maybe binary file --> #{file}" end end}--- Python
まず、カレントディレクトリ以下すべてのファイル(ディレクトリあれば掘る)を出力してみます。使用しているメソッドでは、ドットから始まるファイルも含まれます。
#!/usr/bin/pythonimport osfor dir,dirList,fileList in os.walk("."): for file in fileList: print "%s%s" % (dir,file)ファイル名によるフィルタリング
こんな感じになります。フィルタリングしたいファイル名は、変数 filter に指定します。
#!/usr/bin/pythonimport sys,os,re# 引数指定抜けのときは落とすif len(sys.argv)!=3: print "*E, argument error" sys.exit()# mode(0:ファイル名全体マッチ、1:拡張子を抜いたファイル名にマッチ、2:拡張子マッチ)mode = int(sys.argv[1])filter = sys.argv[2]re0 = re.compile("([\w-]+)\.(\w+)")for dir,dirList,fileList in os.walk("."): for file in fileList: if mode==0: if re.search(filter,file): print file else: m0 = re0.search(file) if m0: root = m0.group(1) ext = m0.group(2) if mode==1: if re.search(filter,root): print file elif mode==2: if re.search(filter,ext): print file含まれる文字列によるフィルタリング+バイナリファイルチェック
Pythonでは、ファイルのバイナリチェックを独自に実装してみました。
*1 def binaryChkの中で使用している ord というメソッドは、コード番号を返すものです
#!/usr/bin/pythonimport sys,os,re### method : binaryChk###################################def binaryChk(filename): infile = open(filename, 'rb') data = infile.read(1024) for byte in data: if ord(byte)>127 : return True infile.close() return False### main process###################################if len(sys.argv) != 2 : print "*E, argument error" sys.exit()filter = sys.argv[1]for dir,dirList,fileList in os.walk("."): for file in fileList: filename = "%s/%s" % (dir,file) if not os.path.islink(filename): if binaryChk(filename): print "maybe binary -> %s" % (filename) else: #print filename hit = 0 for line in open(filename, 'r'): if re.search(filter, line): hit += 1 if hit>0 : print filename