goo에서 디렉터리로 구성된 명령행 도구를 만들었습니다
인도하다
CSV 출력 터미널이나 명령 프롬프트에서 지정한 디렉토리의 구조를 표시하거나 표시할 수 있는 명령줄 도구를 만듭니다.
날과 씨
최근에 IT회사에서 프로그래머로 일했어요.나는 카탈로그 구성도를 만들어 공사 관리를 한다는 것을 처음 알았다.
해설자가 된 사람은 복사 스티커 등으로 빠르게 하고 있다.
직접 만든 프로젝트의 카탈로그 구성도를 만들었으면 좋았겠지만, 다른 사람이 만든 프로젝트를 효율적으로 만드는 것은 저로서는 불가능했기 때문에 명령행 도구를 만들기로 했습니다.
나는 일할 때 Windows를 사용하고 집에서는 Mac를 사용하기 때문에 교차 컴파일할 수 있는 Go로 만든다.
Excel을 사용하여 카탈로그 구성도를 작성하고 공통성을 위해 csv로 출력합니다.
인터넷과 같은 도구가 있을 수 있지만 만드는 것을 좋아하기 때문에 스스로 만든다.
방법
디렉토리 구조
.
├── README.md
├── cmd # コマンドライン
│ ├── cmd.go
│ ├── csv.go
│ └── show.go
├── csv # csvファイル作成
│ └── csv.go
├── dirlist # ディレクトリ構成取得
│ └── dirlist.go
├── go.mod
├── go.sum
├── main.go
└── mymath # 整数関数
└── mymath.go
main.go
이번에는 코브라라는 명령행 도구를 만드는 프로그램 라이브러리를 사용합니다.main.goo에서/cmd/cmd.goo에서 정의한 것만 실행합니다.
main.go
package main
import (
"fmt"
"godeer/cmd"
"os"
)
func main() {
if err := cmd.RootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "%s: %v\n", os.Args[0], err)
os.Exit(-1)
}
}
mymath
아마도 나의 검색량이 부족할 것이다. 그러나 goo의 표준 포장에는 정수 최대치의 함수가 준비되어 있지 않은 것 같다. (부동점수 최대치를 계산하는 함수를 찾았다.)아직 제3자 정돈을 설치할 정도는 아니기 때문에 스스로 함수를 설치한다.
mymath.go
package mymath
// IntMax 引数の内大きい方の値を返す。
func IntMax(a, b int) int {
if a > b {
return a
}
return b
}
cmd
여기에는 코브라 패키지를 사용하여 각종 명령을 실현한다.
이번에 실시해야 할 것은...
루트 명령
이번 명령행 도구의 이름은'gooder'입니다.
cmd.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
func init() {
cobra.OnInitialize()
RootCmd.AddCommand(
showCmd(),
csvCmd(),
)
RootCmd.PersistentFlags().IntP(
"nest",
"n",
5,
"Specify the depth of the directory",
)
RootCmd.PersistentFlags().StringP(
"char",
"c",
"utf-8",
"Select charcter code(utf-8, shift-jis)",
)
}
// RootCmd ルートコマンド
var RootCmd = &cobra.Command{
Use: "godeer",
Short: "directory structure show and output csv",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("godeer")
},
}
csv 명령
csv를 출력할 때의 명령을 실현했습니다.
csv.go
package cmd
import (
"fmt"
"godeer/csv"
"godeer/dirlist"
"godeer/mymath"
"github.com/spf13/cobra"
)
func csvCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "csv",
Short: "dir structure output csv file. arg1: dirpath, arg2: savepath",
Args: cobra.RangeArgs(2, 2),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return nil
}
/* 初期化 */
var err error
var pathString string
var nest int
var savePath string
var pathArray []dirlist.DirStruct
var header []string
var pathLen int
var flg string
/* 引数の取得 */
pathString = args[0]
savePath = args[1]
nest, err = cmd.Flags().GetInt("nest")
if err != nil {
return err
}
flg, err = cmd.Flags().GetString("char")
if err != nil {
return err
}
/* パス配列 */
// 取得
tempArray, err := dirlist.GetDirArray(pathString, nest, flg)
if err != nil {
fmt.Println(err)
return nil
}
// 整形
for _, path := range tempArray {
// 一番深いディレクトリの深さを取得
pathLen = mymath.IntMax(pathLen, len(path.Dir))
}
for _, path := range tempArray {
dl := len(path.Dir)
tempDir := path.Dir
// 深いディレクトリに合わせて空文字を配列に追加
if dl < pathLen {
for i := 0; i < pathLen-dl; i++ {
tempDir = append(tempDir, "")
}
}
// 整形したディレクトリ構造をpathArrayに渡す
pathArray = append(pathArray, dirlist.DirStruct{
Dir: tempDir,
File: path.File,
})
}
// headerの作成
for i := range pathArray[0].Dir {
col := fmt.Sprintf("第%d階層", i+1)
header = append(header, col)
}
header = append(header, "ファイル名")
// 文字コードの変換
var encodeHeader []string
switch flg {
case "shift-jis":
encodeHeader = dirlist.UtoSj(header)
default:
encodeHeader = header
}
/* 出力 */
err = csv.Write(savePath, encodeHeader, pathArray)
if err != nil {
return err
}
return nil
},
}
return cmd
}
표시 명령
디렉터리 구조를 명령줄로 출력할 때의 명령을 실현했습니다.
show.go
package cmd
import (
"fmt"
"godeer/dirlist"
"godeer/mymath"
"github.com/spf13/cobra"
)
func showCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "show",
Short: "show dir structure on array style. arg1: dirpath",
Args: cobra.RangeArgs(1, 1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return nil
}
/* 初期化 */
var pathString string
var nest int
var flg string
var err error
var pathArray []dirlist.DirStruct
var pathLen int
/* 引数の取得 */
pathString = args[0]
nest, err = cmd.Flags().GetInt("nest")
if err != nil {
return err
}
flg, err = cmd.Flags().GetString("char")
if err != nil {
return err
}
/* パス配列 */
// 取得
tempArray, err := dirlist.GetDirArray(pathString, nest, flg)
if err != nil {
fmt.Println(err)
return nil
}
// 整形
for _, path := range tempArray {
// 一番深いディレクトリの深さを取得
pathLen = mymath.IntMax(pathLen, len(path.Dir))
}
for _, path := range tempArray {
dl := len(path.Dir)
tempDir := path.Dir
// 深いディレクトリに合わせて空文字を配列に追加
if dl < pathLen {
for i := 0; i < pathLen-dl; i++ {
tempDir = append(tempDir, "")
}
}
// 整形したディレクトリ構造をpathArrayに渡す
pathArray = append(pathArray, dirlist.DirStruct{
Dir: tempDir,
File: path.File,
})
}
/* 画面出力 */
for _, path := range pathArray {
// 文字コードの変換
var tempDir []string
switch flg {
case "utf-8":
tempDir = path.Dir
case "shift-jis":
tempDir = dirlist.UtoSj(path.Dir)
default:
tempDir = path.Dir
}
deep := 1
// ディレクトリの表示
for _, dir := range tempDir {
fmt.Printf("第%d階層: %10s, ", deep, dir)
deep++
}
// ファイルの表示
fmt.Printf("ファイル: %s \n", path.File)
}
return nil
},
}
return cmd
}
csv
여기에 파라미터가 얻은 경로와 머리 데이터, 신체 데이터를 바탕으로 csv 파일을 출력하는 실시를 실시했다.
이것은/cmd/csv입니다.'고' 에서 읽고 실행합니다.
csv.go
package csv
import (
"encoding/csv"
"godeer/dirlist"
"os"
)
// Write pathに指定したファイルにdataを書き込んで出力
func Write(path string, header []string, data []dirlist.DirStruct) error {
/* 初期処理 */
var err error
var file *os.File
/* ファイルを開く */
file, err = os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return err
}
/* 処理終了後ファイルを閉じる */
defer file.Close()
/* ファイルを空にする */
err = file.Truncate(0)
if err != nil {
return err
}
/* データの書き込み */
// ライターの作成
writer := csv.NewWriter(file)
// ライターバッファ
err = writer.Write(header)
if err != nil {
return err
}
for _, dirpath := range data {
pathArray := dirpath.Dir
pathArray = append(pathArray, dirpath.File)
err = writer.Write(pathArray)
if err != nil {
return err
}
}
// 書き込み
writer.Flush()
return nil
}
dirlist
이것은 테마 디렉터리 구조를 분석하는 실현이다.함수로 분리된 것이기 때문에 함수에 따라 설명한다.
GetDirArray
이것은 기타 포장에서 가장 자주 사용하는 함수다.처리하다
// GetDirArray 引数で指定されたパスのディレクトリ構造を配列で返す。第三引数は文字コードの指定
// 例)
// 引数:. 3 "utf-8" 返り値:[dir dir_a][dir dir_b test.txt][dir dir_b test2.txt]
func GetDirArray(dir string, nest int, char string) ([]DirStruct, error) {
paths, err := dirwalk(dir, nest)
if err != nil {
return nil, err
}
// 文字コードの指定
var encodePaths []string
switch char {
case "utf-8":
encodePaths = paths
case "shift-jis":
encodePaths = UtoSj(paths)
default:
encodePaths = paths
}
pathArray := pathSeparator(encodePaths)
return pathArray, nil
}
dirwalk
지정한 디렉터리에서 지정한 깊이의 디렉터리와 파일 경로의 함수를 되돌려줍니다.
// dirwalk: パスで指定されたディレクトリ内の構造を配列として返す。
func dirwalk(dir string, nest int) ([]string, error) {
var paths []string
// ディレクトリ情報の取得
files, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
if nest == 0 {
// 指定の深さまで達したら現在までのパスを返す。
paths = append(paths, dir)
return paths, nil
}
// 指定の深さに達してなければさらに探索する。
for _, file := range files {
// ファイルパス文字列の作成
filePath := filepath.Join(dir, file.Name())
if file.IsDir() && hasChild(filePath) {
temp, _ := dirwalk(filePath, nest-1)
paths = append(paths, temp...)
continue
}
paths = append(paths, filePath)
}
return paths, nil
}
pathSeparator
dirwalk () 로 상대 경로의 문자열을 얻을 수 있기 때문에 구분자로 나누어 배열에 따라 되돌아오는 함수입니다.
// pathSeparator: パス文字列をセパレーターごとに分けて配列で返す。
// 例)dir/dir_b/test.txt => [dir dir_b test.txt]
func pathSeparator(paths []string) []DirStruct {
var sepPaths []DirStruct
separator := string(filepath.Separator)
for _, path := range paths {
// ディレクトリとファイルを分離
var dir, file string
if filepath.Ext(path) != "" {
// ファイルを指定するパスならディレクトリパスとファイル名に分ける
dir, file = filepath.Split(path)
} else {
// ディレクトリを指定しているパスならfileは空文字
dir = path
file = ""
}
// DirStructに格納
sepDir := strings.Split(dir, separator)
sepPath := DirStruct{
sepDir,
file,
}
sepPaths = append(sepPaths, sepPath)
}
return sepPaths
}
UtoSj
이것은 문자 코드를 고 표준utf-8에서 윈도우즈 처리의shift-jis로 바꾸는 함수입니다.
기능적으로 다른 포장으로 바꿔야 하는데 너무 귀찮아서dirlist에서 실시했습니다.
// UtoSj utf-8 => shift-JISに文字コードを変換
func UtoSj(strArray []string) []string {
var results []string
for _, str := range strArray {
sjStr, _, _ := transform.String(japanese.ShiftJIS.NewEncoder(), str)
results = append(results, sjStr)
}
return results
}
hasChild
위의dirwalk는 인터넷에서 주운 코드를 수정한 것이지만 계속 이러면 파일이 없는 디렉터리를 포함할 수 없기 때문에 디렉터리든 파일이든 작은 요소가 있는 디렉터리를 포함하기 위해 그 기능을 판정해야 한다.
그것은 이 함수다.
// hasChild 引数で指定されたディレクトリがディレクトリまたはファイルと言った子要素を持つかどうかを判定する
// 子要素を持つ場合はtrue, そうでない場合はfalseを返す。
func hasChild(path string) bool {
files, err := ioutil.ReadDir(path)
if err != nil {
panic(err)
}
if len(files) == 0 {
return false
}
return true
}
구축
윈도우즈를 구축하려면 아래 명령을 사용하십시오.
$ GOOS=windows GOARCH=386 go build -o godeer.exe
이렇게 되면 윈도우즈용 명령행 도구가 생겨서 윈도우즈를 통해서만 실행할 수 있다.Mac면 아래처럼 이렇게 지을 수 있어요.$ go build
또는 내 경우/go/bin/경로 통과$ go install
에서 명령행 도구로 즉시 사용할 수 있습니다.시용하다
Mac의 실행 결과를 미리 불러옵니다.
$ godeer --help +[main]
directory structure show and output csv
Usage:
godeer [flags]
godeer [command]
Available Commands:
csv dir structure output csv file. arg1: dirpath, arg2: savepath
help Help about any command
show show dir structure on array style. arg1: dirpath
Flags:
-c, --char string Select charcter code(utf-8, shift-jis) (default "utf-8")
-h, --help help for godeer
-n, --nest int Specify the depth of the directory (default 5)
Use "godeer [command] --help" for more information about a command.
$ godeer show . -n 3 +[main]
第1階層: , 第2階層: , 第3階層: , ファイル: .DS_Store
第1階層: .git, 第2階層: COMMIT_EDITMSG, 第3階層: , ファイル:
# 省略
第1階層: godeer, 第2階層: , 第3階層: , ファイル:
第1階層: , 第2階層: , 第3階層: , ファイル: godeer.exe
第1階層: , 第2階層: , 第3階層: , ファイル: main.go
第1階層: mymath, 第2階層: , 第3階層: , ファイル: mymath.go
당연히 csv를 출력할 수 있고 윈도우즈도 사용할 수 있습니다.감상
생각보다 쉽게 할 수 있어.가장 중요한 것은 맥과 윈도우즈가 모두 고려된 형식으로 만들어졌기 때문에 주의해야 할 부분이 매우 적기 때문에 이렇게 하면 매우 어렵다.
앞으로 공구가 필요하다면 goo로 해보고 싶어요.
Reference
이 문제에 관하여(goo에서 디렉터리로 구성된 명령행 도구를 만들었습니다), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/tomi/articles/2020-12-13-go-cli텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)