treeコマンドの書き方
treeコマンドはツーリー形式でディレクトリ構造を表示するプログラムです。
Windowsにはtreeコマンドが最初からありますが、MacOSやUbuntuにはインストールされていません。
なので、インストールするか自分で書くかということで、最低限ですがRust入門がてら書いてみました。
(初めて書いたプログラムが python で tree コマンドだったと思う)
use std::fs; use std::io; use std::path::{Path, PathBuf}; fn tree(indent: String, dir: &Path) -> io::Result<()> { let mut files: Vec<PathBuf> = Vec::new(); let mut dirs: Vec<PathBuf> = Vec::new(); // 末尾の ? は try! のシンタックスシュガーらしい for entry in fs::read_dir(dir)? { let entry = entry?; let path = entry.path(); // ディレクトリとファイルを分ける // 理由としてはディレクトリが 0個だった場合に // 最後のファイルに └─ を使うようにするため if path.is_dir() { dirs.push(path); } else { files.push(path); } } // ベクターでループ回数が必要な場合は iter().enumerate() する for (i, v) in files.iter().enumerate() { let s: String; if dirs.len() == 0 && i == files.len() - 1 { s = indent.clone() + " └─" } else { s = indent.clone() + " ├─" } output(s, &v); } for (i, v) in dirs.iter().enumerate() { let a: &str; // 次の tree 関数の indent に追加する文字列 if i == dirs.len() - 1 { a = " "; output(indent.clone() + " └─", &v) } else { a = " │ "; output(indent.clone() + " ├─", &v) } tree(indent.clone() + a, &v)?; } Ok(()) } fn output(indent: String, path: &PathBuf) { // OsString を String にする // WindowsではUTF-16で解釈される場合があるので、Unixとの差を埋めるためにOsStringがあるらしい let s = path.file_name().unwrap().to_string_lossy(); println!("{} {}", indent, s); } fn main() { const ROOT: &str = "."; println!(" {}", ROOT); if let Err(err) = tree(String::new(), &Path::new(&ROOT)) { println!("Error: {}", err); } }
├─
や └─
は box-drawing character というものらしいです。
同じコードをGo言語で書いた場合
package main import ( "fmt" "log" "os" "path/filepath" ) func readdir(name string) ([]string, []string, error) { fp, err := os.Open(name) if err != nil { return nil, nil, err } list, err := fp.Readdir(-1) fp.Close() if err != nil { return nil, nil, err } dirs, files := []string{}, []string{} for _, v := range list { if v.IsDir() { dirs = append(dirs, v.Name()) } else { files = append(files, v.Name()) } } return dirs, files, nil } func tree(indent, path string) error { dirs, files, err := readdir(path) if err != nil { return err } for i, v := range files { s := indent + " ├─" if len(dirs) == 0 && i == len(files)-1 { s = indent + " └─" } fmt.Printf("%s %s\n", s, v) } for i, v := range dirs { s := indent + " ├─" a := " │ " if i == len(dirs)-1 { s = indent + " └─" a = " " } fmt.Printf("%s %s\n", s, v) if err := tree(indent+a, filepath.Join(path, v)); err != nil { return err } } return nil } func main() { const Root = "." fmt.Printf(" %s\n", Root) path, err := filepath.Abs(Root) if err != nil { log.Fatal(err) } if err := tree("", path); err != nil { log.Fatal(err) } }