<ruby> grep ぽいことするやつ作った

新しいMac買ってしまった
お金やばいのも多少あるけどそれ以上にサクサク動いて快適

grepぽいことするやつ作った

grepってコマンドあるじゃないですか。
ファイルの中身とか検索するあれ
あれってwindowsで使えないじゃないですか
互換はあるけど使い方で見れば別物なのでなんともっていう

そこで同じように使うとなったらプログラム書くしかなくねとなり書きました。

目次

  1. 今回の目的
  2. 作ったもの
    2.1 ファイルの取得
    2.2 取得したファイルの検索
    2.3 検索結果の出力
    3.できたもの

1.今回の目的

目的はほぼ最初のところに書いてしまっている。
なのでさらに詳細まで書きます。
今回の最終的な目的としてはドラッグ&ドロップで対象ファイル選んでキーワード、条件に一致するやつを検索するようなものを完成させるのが目的になっています。
最初っから一気に完成形を目指せる自信は皆無なのでまずはコマンドで使えるようになればいいかなということで作成します。
今回のゴールとしては

grep-test poolbooyer$ grep -n index data/* 
data/sample1.txt:3:index
data/sample1.txt:4:index
data/sample1.txt:5:index 
data/sample1.txt:6:/index.htm
data/sample1.txt:7:index
data/sample1.txt:9:/index.html
data/sample1.txt:10:/index.htm
data/sample2.txt:3:/assets/js/index.js
...

的なものを作ります。
ただ、このままだと微妙に見づらさがあるので、出力形式を以下のようにします。

file名
行数:ヒットした文字列
行数:ヒットした文字列
file名
行数:ヒットした文字列
...

なんかいい感じに同じファイル単位でまとめて表示するようにします。

2.作ったもの

 ということで実装していきました。
今回の流れとしては

ファイル一覧取得->ファイル内容取得->ファイル内部検索->結果出力

でいきます。
この時検索する語句と対象はコマンドライン引数で引っ張ってきます。

ruby serch-eg.rb フォルダ名 検索語句

最初二つはDirで取得して、fileopenなので今回は
ちゃんと書きます

2.1 ファイルの取得

まずはファイルの情報を取得します。
検索対象のフォルダの中身を確認します。
その後、検索対象の中身を取得します。
今回はファイル名とファイルの中身を1つの配列で返すようにしました。
まずはフォルダの中身の確認と取得するところを呼び出すところまで

# 引数のフォルダの中身を確認する
fi=Dir::entries(ARGV[0])
  # 結果を取扱う配列
  data=[]
  # フォルダ中身全てに対して
  for i in 0..(fi.length-1) do
    if fi[i].length>2 then
      # ファイルの中身を呼び出して配列に追加
      data.push(read_file(ARGV[0]+fi[i]))
    end
  end

read_fileに渡しているARGV[0]+fi[i]はファイルへのパスです

その上で読み出すプログラム

def read_file(path)
  r_data=[]
  r_data[0]=path
  File.open(path,"r") do |f|
    r_data[1]=f.read
  end
  # 読み込んだデータとファイルのパスを返す
  return r_data
end

的な感じです

2.2 ファイルの検索

読み込んだ情報をもとにしてその中身を検索します。

targetには読み込んだデータ、queryには検索する文字列が入っています。

def search_infile(target,query)
  # 検索結果
  res=[] 
  for i in 0..(target.length-1) do
    # 文字列を含むか検索
    if target[i].include?(query) then
      res[i]=true
    else
      res[i]=false
    end
  end
  # 検索結果を返す
  return res
end

2.3 結果の出力

 検索と出力が1つのプログラムにまとめてます。
ここは分割する予定です。
検索するプログラムにコマンドライン引数で受け取った検索文字列と読み込んだファイルのデータを渡すところから

  query=ARGV[1]
  search_each(data,query)

そして、2.2のプログラムを呼び出します

def search_each(data,query)
  data.each do |line|
    # line[1]にあるファイルの中身を改行単位で配列に分割
    target=line[1].split("\n")
    # 検索結果を取得
    res=search_infile(target,query)
    # 検索結果をカウント
    count=0

    # ファイル名称を出力
    puts line[0]
    #  検索結果を参照
    for i in 0..(res.length-1) do
      # 一致していたら出力
      if res[i]==true then
        puts "L#{i+1}: #{target[i]}"
      else
        count+=1
      end
    end
    # 1つも一致しなかったら"Not found"と出力
    if count==(res.length) then
      puts "Not found"
    end
    # ファイルの結果の間に一行あける
    puts ""
  end
end

3.できたもの

ということでこれを実行するとこんな感じになります

grep-test poolbooyer$ ruby ./searcher/search-eg.rb data/ inde
data/sample4.txt
L1: index
L2: /index.htm
L3: index
L7: index
L8: /assets/js/index.js
L9: index
L10: /index.html

data/sample5.txt
L1: inde
...

grep-test poolbooyer$ ruby ./searcher/search-eg.rb data/ fuga
data/sample4.txt
Not found

data/sample5.txt
Not found

data/sample1.txt
...

これで期待する結果の出方になりましたー!
ちなみに今回のコードは以下にあります

github.com (search/に入ってます、検索対象にするデータはscript/generator.rbを使って生成してます)
まだ直しが入る予定なので今回はここまで