はじめに
Goを勉強したいので、手始めにls
コマンドを一部実装してみようかなと思います。今回は、ls
でファイル名の一覧が表示されること、またオプションは、-l
でファイルの詳細一覧が出力されることを目標として設定します。
実装
今回のリポジトリですmm
では最初に、ls
を実装していきます。ポイントは、os
ライブラリが提供しているos.Getwd()
でカレントディレクトリのパスを取得し、os.ReadDir()
でディレクトリ内のファイルを全て返して、ファイル名を出力しているようにしている点です。
package main
import (
"fmt"
"os"
)
func main() {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
files, _ := os.ReadDir(dir)
listFiles(files)
}
func listFiles(files []fs.DirEntry) {
for _, f := range files {
fmt.Printf("%s ", f.Name())
}
}
では、スクリプトを実行してみます。
$ go run main.go
README.md go.mod main.go
ここまでは、上手くいっていそうです。つづけて、オプションの-l
を実装します。
ポイントは、flag
ライブラリが提供するflag.Bool()
でコマンドライン引数を受け取るようにし、fs
ライブラリのfs.DirEntry
といったインターフェースに属する関数を活用し、ファイルの各種情報を出力している点です。
package main
import (
"flag"
"fmt"
"io/fs"
"os"
"time"
)
func main() {
+ isListed := flag.Bool("l", false, "List files")
+ flag.Parse()
dir, err := os.Getwd()
if err != nil {
panic(err)
}
files, _ := os.ReadDir(dir)
+ if *isListed {
+ listFilesWithDetails(files)
+ return
+ }
listFiles(files)
}
func listFiles(files []fs.DirEntry) {
for _, f := range files {
fmt.Printf("%s ", f.Name())
}
}
+func listFilesWithDetails(files []fs.DirEntry) {
+ for _, f := range files {
+ info, err := f.Info()
+ if err != nil {
+ panic(err)
+ }
+ fmt.Printf("%s %s %d %s\n", info.Mode(), formatDate(info.ModTime()), info.Size(), f.Name())
+ }
+}
+func formatDate(t time.Time) string {
+ return t.Format("Jan 02 15:04")
+}
では、スクリプトを実行してみます。
$ go run main.go -l
-rw-r--r-- May 16 22:51 8 README.md
-rw-r--r-- May 16 18:18 30 go.mod
-rw-r--r-- May 18 15:42 719 main.go
こちらも、期待通りの結果が返ってきました。なお、今回は本来の-l
にあるような、ハードリンクの数や所有者・所有グループまでは実装しませんでした。特に後者の理由は、Goの標準ライブラリのみではシンプルには書けないようだからです。Goの限界なのでしょう。
おわりに
こうして実装に取り組むと学びが色々とあると感じますし、やはり面白いです。
アドバイスや指摘があれば、頂けると幸いです。次は、grep
コマンドの実装に挑戦してみようかな~