wakasa5の日記

駆け出しエンジニアの勉強録

Goで実装するLinuxコマンド ~ cat拡張編 ~

前回の続きで、catコマンドを拡張する試み行いました。


スターティングGoを読んで、少しGoの基礎知識を入れましたが、全然まだまだ理解できていないです…笑


つらいし難しいけど楽しい…

複数ファイルの対応

前回のcatコマンドは、単体ファイルしか読めませんでした。


それだとconcatenateではないですよね…笑


というわけで複数ファイルを読み込めるようにしました。

package main

import (
	"io"
	"log"
	"os"
)

func main() {
	filenames := os.Args[1:]
	files := make([]io.Reader, len(filenames))

	for i, filename := range filenames {
		file, err := os.Open(filename)
		defer file.Close()
		files[i] = file
		if err != nil {
			log.Fatalln(filename, "is empty")
		}
	}

	reader := io.MultiReader(files...)
	io.Copy(os.Stdout, reader)
}

前回はio.Readerを使いましたが、今回は複数ファイルを読み込めるように、io.MultiReaderを使っています。


また前回はos.Args[1]だけを読み込んでいましたが、os.Args[1:]でos.Args[1]以降のスライスを切り出して、それぞれをos.Openで開いています。


また前回はlog.Fatalln()を使っていましたが、埋め込みを使いたかったので、log.Fatalf()を使うようにしました。


log.Fatal系ってPrintに対応した感じになっているみたいですね。

(Println <=> Fatalln, Printf <=> Fatalf)

参考:https://golang.org/pkg/log/#Fatalf


上記の実装で複数ファイルでも動くようになりました。

オプションパラメーターを渡す


当たり前ですが、Linuxコマンドはオプションを渡せますよね。


Goにはflagという便利なものがあるので、オプションパラメーターも比較的簡単に扱えます。


kubernetesやdockerのコマンドもこれ使っているのかな〜と思ったり…?


catコマンドは本来的には色々とパラメーターがありますが、今回は一番簡単そうな-nオプションを実装してみることにしました。


ちょっとオプションのところが拡張がつらい実装ですが…

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
)

var (
	numOpt = flag.Bool("n", false, "add num of column for n option")
)

func main() {
	flag.Parse()

	filenames := flag.Args()
	files := make([]io.Reader, len(filenames))

	for i, filename := range filenames {
		file, err := os.Open(filename)
		defer file.Close()
		files[i] = file
		if err != nil {
			log.Fatalf("%s is empty", filename)
		}
	}

	reader := io.MultiReader(files...)
	if *numOpt == true {
		scanner := bufio.NewScanner(reader)
		i := 1
		for scanner.Scan() {
			line := scanner.Text()
			fmt.Println(i, line)
			i++
		}
	} else {
		io.Copy(os.Stdout, reader)
	}
}

flagオプションを使って、オプションを取り出せるようになっています。


ちゃんと-hオプションで設定したhelpが見れるのはすごい…flagすごい…
(英語が適当…笑)

$ ./cat -h
Usage of ./cat:
  -n	add num of column for n option


また今回は行番号を頭につけるためにbufio.NewScanner()を使っています。


それで頭に行番号をつけて一行ずつ標準出力に出すようにしています。


これで-nオプションをつけて、複数ファイル渡しても、ちゃんと番号付きでラインが出るようになりました!

$ ./cat -n sample-txt/cat.txt sample-txt/dog.txt
1 cat
2 dog
3 egg
4 dog
5 dog
6 dog

終わりに

まだまだGo言語慣れていないですが、こうやって調べながら試行錯誤するのは楽しいですね。


rebuildのruiさんの回を聴きながら楽しく作業していました。笑


あとオプション作るの大変なんだなと改めて思いました。。


Linuxはどうやって複数オプション処理しているのか気になる…


今日はこの辺で。


最後まで見ていただきありがとうございました!