goo에서 디렉터리로 구성된 명령행 도구를 만들었습니다

57114 단어 GoCLItech

인도하다


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


여기에는 코브라 패키지를 사용하여 각종 명령을 실현한다.
이번에 실시해야 할 것은...
  • 명령 주체로서의 루트 명령
  • csv 출력 csv 명령
  • 디렉토리 구조를 표시하는 Show 명령
  • 네.

    루트 명령


    이번 명령행 도구의 이름은'gooder'입니다.
  • 각종 명령(csv,Show), 로고의 초기화
  • 루트 명령(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
    }
    

    표시 명령


    디렉터리 구조를 명령줄로 출력할 때의 명령을 실현했습니다.
  • 변수의 초기화
  • 매개 변수, 표지 가져오기
  • 디렉토리 구조 가져오기
  • 화면 출력
  • 이런 절차로 처리하다.단지 화면 출력 처리를 위해 csv에 들어갔을 뿐 처리는 아무런 변화가 없다.
    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로 해보고 싶어요.

    좋은 웹페이지 즐겨찾기