シェルスクリプトからid作成(GO言語を使った方法)

以前シェルスクリプトから
idを作成するものを書いたが、
csvパーサーがないとまずできないので、
結果として
pythonを使った。

ただ、pythonはこのような処理を書くのに根本的に
向かないため、GO言語で書き直すことにした。


なぜGO言語か
なぜ、簡単に使えるpython,ruby,perlでなく
いちいちビルドしないといけないコンパイル言語を使うか
というと、

  1. csvの長さ如何によってはメモリ、速度、バッファの長さを厳しい。
  2. 細かい制御がきかない
  3. そもそもスクリプト言語はコマンド等を作るのに向かない。

1−3の理由のため、C言語を使うことも考えた。
どのOSでも標準で備えているし、コンパイルさえすれば、
同じコードで動くからだ。

結局OSごとにライブラリが違うから
他のもので書いたほうが良いという人もいるかもしれない。
しかし、
C言語の標準ライブラリを見てみるとわかるが、
文字列、メモリ関係など明らかに
シェルのコマンド,デバイスファイル等を作ることを意識したものが多く、
事実BSD,Linux等OSのコマンドはほとんどC言語で描かれている。

これは、C言語はそもそもOSを作る言語であったことに由来している。

実行速度が早いからC言語を使うという人が多いが、
それは
OSを作りたい→ある程度の速度が必要→C言語
という
結果として早い言語であるC言語を選択した点を忘れている。

じゃあC言語でかけば良いじゃないか
というかもしれないが、
C言語で書こうとすると
csvパーサーが標準ライブラリにないので
作業効率が悪い。
(C言語の標準ライブラリにあるものなら、
GO言語で書くよりC言語で書いたほうが、
言語の安定性、マルチプラットフォームという意味でよい。
また、作業効率でもそんなに変わらないはず。)

また、Windowsで実行することを考えると、
VisualC++等は最近あまり動きがない上、
C#,Powershellの方にMS
が興味がある。
また、VisuallC++をやろうとすると
どうしてもVisual studioをインストールする必要が出る。

Windowsで
C言語を使うなら、
wslのbashでgccを使うのがよい。



そこで、csvパーサーを持っていて、
C言語の役割を置き換えることを目指している
Go言語を使うことにする。

Go言語はC言語よりもコミュニティという意味でも安定している。
(C言語はおそらくQtにコミュニティが集中しているが、
QtはフリーのものはGPLライセンス。
TrueOSに使われている部分等BSDライセンスの部分もあるらしいが...
GO言語も一部GPLライセンスらしいので注意すること
http://sssslide.com/speakerdeck.com/nabeken/go-and-license

前回の結構苦労した
csvのid列の追加をgo言語だと下のように
かける
package main

import (
    "encoding/csv"
    "flag"
    "fmt"
    "io"
    "os"
    "strconv"
    "strings"
)

//ls -1 | xargs ls -1 | sed 's/:$/\//' | ./conbertfrom_csv/convertfrom_csv
var (
    icm = flag.Bool("c", false, "help message for b option")

    delimiter = flag.String("d", ",", "help message for d option")
)

//文字列の途中でカラムを改行する場合は、"""をつけること。でないと正しく動かない。

//ls -1 | xargs ls -1 | ./conbertfrom_csv/convertfrom_csv
//deli=$(echo -e "\t")
//cat staff.1.csv | go run convertfrom_csv.go -c -d "${deli}"


//全くからの行は無視するように作っている。
func main() {

    //これがないとオプション引数が展開されないので注意。
    flag.Parse()

    var err error
    //ひとつの行のカラムの長さも考える必要があるかも。
    var row []string

    //csvのデリ見たが指定できていないのであとでしてい。
    //csvを読み込むために必要
    cin := csv.NewReader(os.Stdin)

    //CSV.Commaもデリミタは1バイトで決め打ちしている。ためこれでよい。
    //string[0]で勝手にbyteにキャストされるので後で変換する必要がある。
    deli := (*delimiter)[0]
    //runeで入れる必要がある。runeもstringもバイト列のためこのようなことができる。
    cin.Comma = rune(deli)
    //forの三項目はfor文の最初に評価されるのでここでも一度読んでおく。
    row, err = cin.Read()

    //ストリームの最後まで読んだら、ENDになる。
    for i := 0; err != io.EOF; row, err = cin.Read() {

        //EOF意外のエラーだった場合は標準エラー出力に吐き出す。
        if err != io.EOF && err != nil {
            fmt.Fprintln(os.Stderr, "CSV形式のデータを正しく読み取れませんでした。")
            os.Exit(1)

            //もしくは下で処理をやめてシェル自体を継続させる。
            //fmt.Fprintln(os.Stderr, "正しく読み取れなかった可能性があるデータがあります。")
            //break;
        }

        //id列の追加部分ここだけユニーク
        if i == 0 && *icm {

            fmt.Print("id" + string(deli))
        } else if i != 0 && *icm {
            fmt.Print(strconv.Itoa(i) + string(deli))
        } else {
            fmt.Print(strconv.Itoa(i+1) + string(deli))
        }

        //csv形式の行をjoinでまとめている。
        fmt.Println(strings.Join(row, string(deli)))

        i++
    }

}

$ go build pasteid
でコンパイルして、
pasteidファイルを作成して、
これをコマンドとして使う。

$ cat csvファイル(カラム名付き) | pasteid -c
と実行してほしい。
id列が追加したcsvが標準入力に描かれるはずだ。

カラム名がないcsvファイルなら下のようにする。
$ cat csvファイル(カラム名なし) | pasteid 

(pasteidコマンドへのパスを通すこと。同じディレクトリにいるなら、
./pasteidでよい。)
-dオプションでデリミタを指定すること
も出来る。


今後
ソースをクリーンアップして、
csvを処理する
bash用のライブラリを
go言語で作ってgithubに公開するので

興味がある人はぜひ見てほしい。



コメント

人気の投稿