- Ruby の勉強を目的に ls コマンドを再発明
- doc
- YARD 形式ドキュメント
- test
- テスト用スクリプトとテスト結果
- README.org
- この Readme
- rls
- Ruby を用いた ls コマンド
- 下記オプションを実装
- -a
- -r
- -l
- OS
- CentOS Linux 7.6.1810
- Ruby
- ruby 2.6.3p62
- ls のリファレンス実装
- GNU coreutils version.8.22-21
- [ ] ロケール対応(メッセージ、日付フォーマット)
- テストは LANG=C 固定で実施
- [ ] ls -l の SELinux セキュリティコンテキスト表示(バーミッション直後のドット表示)
ls -l / total 32 lrwxrwxrwx 1 root root 7 Sep 28 2018 bin -> usr/bin dr-xr-xr-x. 5 root root 4096 May 23 21:24 boot パーミッションの後のドット- セキュリティコンテキスト表示は無い前提でテストする
- [ ] オリジナルの ls -l で同じファイルのカラム表示位置がずれる場合がある(テストケース case.03)
- 表示対象ではないファイルのユーザ名等の長さがカラム幅に反映されている?
$ ls -l test.case.03.sh -rwxr-xr-x 1 dec dec 501 Jul 10 23:56 test.case.03.sh $ ls -l test.case.03.sh /root -rwxr-xr-x 1 dec dec 501 Jul 10 23:56 test.case.03.sh ls: cannot open directory /root: Permission denied $ ls -l test.case.03.sh /var/log/httpd/ -rwxr-xr-x 1 dec dec 501 Jul 10 23:56 test.case.03.sh $ ls: cannot open directory /var/log/httpd/: Permission denied
- 表示対象ではないファイルのユーザ名等の長さがカラム幅に反映されている?
- [ ] オリジナルの ls でパイプに通すとエラーメッセージが表示されなくなる場合がある(テストケース case.03)
- 実行権限無しディレクトリ中にあるファイルを対象としたエラーメッセージだけ?
パイプに通すとエラーメッセージが表示されなくなる例 $ ls ./dir.not.executable/ ./not.exist ls: cannot access ./not.exist: No such file or directory ./dir.not.executable/: ls: cannot access ./dir.not.executable/file01: Permission denied ls: cannot access ./dir.not.executable/file02: Permission denied file01 file02 $ ls ./dir.not.executable/ ./not.exist | cat ls: cannot access ./not.exist: No such file or directory ./dir.not.executable/: file01 file02 同じパスで -l を付けるとパイプに通しても全てのエラーメッセージが表示される $ ls -l ./dir.not.executable/ ./not.exist ls: cannot access ./not.exist: No such file or directory ./dir.not.executable/: ls: cannot access ./dir.not.executable/file01: Permission denied ls: cannot access ./dir.not.executable/file02: Permission denied total 0 ?????????? ? ? ? ? ? file01 ?????????? ? ? ? ? ? file02 $ ls -l ./dir.not.executable/ ./not.exist | cat ls: cannot access ./not.exist: No such file or directory ls: cannot access ./dir.not.executable/file01: Permission denied ls: cannot access ./dir.not.executable/file02: Permission denied ./dir.not.executable/: total 0 ?????????? ? ? ? ? ? file01 ?????????? ? ? ? ? ? file02
- 実行権限無しディレクトリ中にあるファイルを対象としたエラーメッセージだけ?
- 思いついたケースを試してみたレベルです。プロダクションレベルのテストは実施できておりません。
- [X] オプション無し :case.00:
- [X] ‘-a’, ‘-l’, ‘-r’ それぞれを単体で指定 :case.01:
- [X] ‘-al’ や ‘-alr’ などのように組み合わして指定 :case.00:
- [X] パスの前後に指定 :case.00:
- [X] ‘–’ の後に書いたオプションはパス扱いになること :case.00:
- [X] ‘- a’ はパス扱いになること :case.00:
- [X] パス無し :case.01:
- [X] 単体で指定 :case.01:
- [X] 複数指定 :case.01:
- [X] 相対パス :case.01:
- [X] 絶対パス :case.01:
- [X] ファイル :case.01:
- [X] ディレクトリ、最後 / 無し :case.01:
- [X] ディレクトリ、最後 / 有り :case.01:
- [X] ディレクトリ/* :case.01:
- [X] * :case.02:
- [X] キャラクタデバイス、ブロックデバイス、fifo、ソケット、シンボリックリンク :case.04:
- [X] 実行権限無しのディレクトリ :case.03:
- [X] 実行権限無しのディレクトリ中のファイル :case.03:
- [X] パイプ、ファイルへの出力が 1カラムになること :case.01:
- [X] 一行で画面幅に収まるパターン :case.05:
- [X] 複数行にわたるパターン :case.05:
- [X] 同じパス指定で画面幅を変更して行数が変更になるパターン :case.05:
- [X] 最大ファイル名長が画面幅を超えるパターン :case.05:
- [X] 日付 182日以上過去/未来は時刻ではなく年を表示 :case.01:
- [X] スティッキービット、setuid、setgid の表示 :case.03:
- [X] ディレクトリ中のシンボリックリンクの表示 :case.01:
- [X] シンボリックリンク単体の表示 :case.04:
- [X] ディレクトリ表示の場合に合計ブロック数表示 :case.01:
- [X] ファイルのみの表示の場合には合計ブロック数表示無し :case.01:
- [X] ファイル種類、パーミッション、ハードリンク数、ユーザ名、グループ名、サイズ、最終更新日、ファイル名の表示 :case.01:
- [X] 複数ディレクトリ表示 :case.01:
- [X] -a 有りと無しのパターン :case.00:case.01:
- [X] -r 有りと無しのパターン :case.00:case.01:
- [X] 上位ディレクトリに実行権限が無い場合の cannot access Permission denied エラー :case.03:
- [X] 存在しないファイル、ディレクトリ時の cannot access No such file or directory エラー :case.03:
- [X] パスに指定したディレクトリに読み込み権限が無い場合の cannot open directory Permission denied エラー :case.03:
- [X] 存在するファイルと同名のパスを / 付きで指定した際の cannot access Not a directory エラー :case.03:
- test/ ディレクトリ以下にテストケース毎に別ディレクトリで格納
- ls と rls の結果の diff を取るシェルスクリプトを作成し、diff の結果が無いことをテスト OK とする。
- test.case.nn.sh テスト実行シェルスクリプト
- results/
- dif.err.* 標準エラー出力の差異
- dif.out.* 標準出力の差異
- ols.err.* オリジナルの ls の標準エラー出力
- ols.out.* オリジナルの ls の標準出力
- rls.err.* 今回作成 rls の標準エラー出力
- rls.out.* 今回作成 rls の標準出力
- リダイレクトではなく画面表示のテスト(case.05)は、ターミナルに出力した結果を手作業で貼り付け
- case.00
- オプションのパース
- case.01
- パスのパース
- パス、ファイルの種類
- -l オプション無しカラム表示でのパイプ、ファイルへの出力
- -l オプションありリスト表示
- -a オプションあり/なし
- -r オプションあり/なし
- case.02
- パスに * のみ指定
- テスト結果で生成されるファイルがテストに影響を与えるため、結果は dir01/results/ に格納
- case.03
- エラー表示
- -l オプションありリスト表示でのスティッキービット、setuid、setgid の表示
- 実行権限無しのディレクトリ、およびそのディレクトリ中のファイル
- case.04
- キャラクタデバイス、ブロックデバイス、fifo、ソケット、シンボリックリンク
- case.05
- -l オプション無しカラム表示での画面への出力
- テスト結果は results.txt
- `ls`コマンドを作成
- オプションとして、最低限 `-a`, `-l`, `-r`を実装。`-al`や`-alr`などのように、組み合わされても使えるように。
- 言語は最新版のRuby、あるいは、最新版のPHP
- GitHub上のリポジトリとして、コードを提出
- 納期 7月12日(金)17:00
- 既存アカウントを使用
- 19/07/01 現在 Ruby 2.6.3
- 19/07/01 現在 PHP 7.3.6
- 開発中にさらなる最新バージョンがリリースされた場合に必要
- 必要となる可能性低
- インタプリタにソース読ませるのではなく、単体のコマンドとして実行する方法
- シェルスクリプトではダメ?
- シーバンで可
- #!/usr/bin/env ruby
- Ruby
- ARGV[]
- Ruby
- strftime()
- format()
- ターミナルの問題。エスケープ文字出力で可能か?
- 色付けなしがデフォルトなのでこれは考えない
- Ruby
- File.stat()
- そういう話じゃない
- ls の出力と作成プログラムの出力の diff で確認
- [X] path にワイルドカードがあったときの対応←シェルの仕事では?
- [X] ファイル一覧表示
- ファイルは一つのファイル一覧に追加、ディレクトリはパスとファイル一覧のセットを保持
- まずファイル一覧を処理、ファイル一覧に内容が無くパスとファイル一覧のセットが一個だけならそれをファイル一覧として処理
- パスとファイル一覧のセットが複数あればパスを表示してファイル一覧を処理をセット分繰り返す
- Dir.entries はディレクトリが対象でファイルは対象ではない
- Dir.glob で path を一個ずつ取得し、ディレクトリかファイルかで処理を分ける?
- 結果はこう
ls ../golang/ choice main main.go main.go~ processlog ls ../golang/choice/choice ../golang/main.go ../golang/choice/choice ../golang/main.go ls ../golang/* ../golang/main ../golang/main.go ../golang/main.go~ ../golang/choice: choice main.go main.go~ ../golang/processlog: arg.go ls ../golang/choice/choice ../golang/main.go ./ ./temp01.rb ./* ../golang/choice/choice ../golang/main.go ./temp01.rb ./temp01.rb ./temp01.rb~ ./temp02.rb ./temp02.rb~ ./: temp01.rb temp01.rb~ temp02.rb temp02.rb~
- [X] path が指定されなかった場合はカレントディレクトリが対象
- [X] path が存在しなかったらエラー表示
- [-] 権限がない場合もエラー
- path を絶対パスに変換し、最上位ディレクトリから順に検査、読み取り権限と実行権限が無ければ権限エラーで終了
- を開くことが出来ません: 許可がありません
- 順に下位のディレクトリを同様に検査
- ディレクトリの中の最下位のものは読み取り権限が無いだけで権限エラーで終了
- を開くことが出来ません: 許可がありません
- path の最下位がディレクトリの場合で実行権限がなければ、その中のファイル一覧は表示しつつ、それぞれの権限エラーも表示
- にアクセスできません: 許可がありません
- path の最下位がファイルの場合はその上のディレクトリに実行権限がなければ権限エラー
- にアクセスできません: 許可がありません
- [X] 結果はこう
ls -a /var/log/httpd/access_log ls: /var/log/httpd/access_log にアクセスできません: 許可がありません ls -a /var/log/httpd/ ls: ディレクトリ /var/log/httpd/ を開くことが出来ません: 許可がありません- [X]
ls ../test/rx 01rx 02rx rls ../test/rx 01rx 02rx- [ ] 最下位が実行権限のないディレクトリの場合。エラーメッセージ、ディレクトリ名の表示有無が異なる
ls ../test/r ls: cannot access ../test/r/rx: Permission denied ls: cannot access ../test/r/r01: Permission denied ls: cannot access ../test/r/r02: Permission denied ls: cannot access ../test/r/r03: Permission denied ls: cannot access ../test/r/rxw: Permission denied r01 r02 r03 rx rxw rls ../test/r ls: cannot access ../test/r/.: Permission denied ls: cannot access ../test/r/..: Permission denied ls: cannot access ../test/r/r01: Permission denied ls: cannot access ../test/r/r02: Permission denied ls: cannot access ../test/r/r03: Permission denied ls: cannot access ../test/r/rx: Permission denied ls: cannot access ../test/r/rxw: Permission denied ../test/r: r01 r02 r03 rx rxw - path を絶対パスに変換し、最上位ディレクトリから順に検査、読み取り権限と実行権限が無ければ権限エラーで終了
- [X] 先頭がハイフンのファイル名の対応
- – の後のパラメータを path として扱うことで OK
- [X] ハイフン一個のみはファイル名として扱う
- [X] キャラクタデバイス
- [X] ブロックデバイス
- [X] fifo
- [X] ソケット
- [X] シンボリックリンクの処理
- オプション -l のとき
- [X] ファイル名にリンク先追加
- [X] シンボリックリンクのパスを単体で与えた時、リンク先の一覧を表示してしまう
- [X] シンボリックリンク /etc/grub2.cfg の表示がおかしい。
ls -l /etc/grub2.cfg lrwxrwxrwx 1 root root 22 May 8 20:01 /etc/grub2.cfg -> ../boot/grub2/grub.cfg ./rls -l /etc/grub2.cfg ls: cannot access /etc/grub2.cfg: No such file or directory
- [X] /dev の表示形式が異なる
- [X] – でオプション終了の対応
- [X] -a 対応
- [X] ls .*
- [X] -r 対応
- [X] -l 対応
- [X] 日付 182日以上過去/未来は時刻ではなく年を表示
- [X] スティッキービット、setuid、setgid 対応
- [X] ls -s のブロック数と File.stat().blocks の数が合わない。
- File.stat().blocks が 2倍の値になる?
- [X] x なしディレクトリ中のファイル一覧で stat しようとしてパーミッションエラーになる。先にアクセス可能かテストする。だめなら例外で対応
rls -l ../test/r ls: cannot access ../test/r/.: Permission denied ls: cannot access ../test/r/..: Permission denied ls: cannot access ../test/r/r01: Permission denied ls: cannot access ../test/r/r02: Permission denied ls: cannot access ../test/r/r03: Permission denied ls: cannot access ../test/r/rx: Permission denied ls: cannot access ../test/r/rxw: Permission denied ../test/r: Traceback (most recent call last): 8: from ./rls:356:in `<main>' 7: from ./rls:356:in `each_with_index' 6: from ./rls:356:in `each' 5: from ./rls:359:in `block in <main>' 4: from ./rls:150:in `print_dirs' 3: from ./rls:105:in `print_files' 2: from ./rls:105:in `map' 1: from ./rls:105:in `block in print_files' ./rls:105:in `stat': Permission denied @ rb_file_s_stat - ../test/r/r01 (Errno::EACCES) ls -l ../test/r ls: cannot access ../test/r/rx: Permission denied ls: cannot access ../test/r/r01: Permission denied ls: cannot access ../test/r/r02: Permission denied ls: cannot access ../test/r/r03: Permission denied ls: cannot access ../test/r/rxw: Permission denied total 0 ?????????? ? ? ? ? ? r01 ?????????? ? ? ? ? ? r02 ?????????? ? ? ? ? ? r03 ?????????? ? ? ? ? ? rx ?????????? ? ? ? ? ? rxw
- [ ] テストケース洗い出し
- [X] ls -C 動作再現
- [X] カラム数決定ロジック
- ls /lib
- [X] 整列表示
- [X] カラム数決定ロジック
- [X] ファイルやパイプに繋いだ時の表示対応
- STDOUT.isatty
- [X] エラーメッセージと結果の表示順
- [X] 下記の順?
- ls: cannot access : Permission denied
- ls: cannot access : No such file or directory
- ファイルリスト
- ls: cannot open directory
- ディレクトリリスト
- [X] oioi と /etc/oioi の順番が合わない、パラメータ出現順?
ls -a /etc/oioi oioi ls: cannot access /etc/oioi: No such file or directory ls: cannot access oioi: No such file or directory ls -a oioi /etc/oioi ls: cannot access oioi: No such file or directory ls: cannot access /etc/oioi: No such file or directory - [X] 結果例
ls -a rls /root/temp /var/log/httpd ls: cannot access /root/temp: Permission denied rls ls: cannot open directory /var/log/httpd: Permission denied - [X] 結果例
ls -a rls root.thing root.thing rls: . .. .git .gitignore LICENSE README.org rls rls~ ls -a rls root.thing oioi ls: cannot access oioi: No such file or directory root.thing rls: . .. .git .gitignore LICENSE README.org rls rls~ ls -a root.thing oioi ls: cannot access oioi: No such file or directory root.thing ls -a rls oioi ls: cannot access oioi: No such file or directory rls: . .. .git .gitignore LICENSE README.org rls rls~
- [X] 下記の順?
- [X] エラーメッセージの順番をオリジナルに合わせる
オリジナル cannot open directory XXX: Permission denied 以外のエラーメッセージはパラメータの出現順 cannot open directory XXX: Permission denied はファイル一覧の後に出力 今回作成版 cannot open directory XXX: Permission denied 以外のエラーメッセージは以下の順に出力、同じ種類のエラーはパラメータ出現順 cannot access %s: No such file or directory cannot access %s: Not a directory cannot access %s: Permission denied cannot open directory XXX: Permission denied はファイル一覧の後に出力 $ls /root/not.exist not.exist LICENSE/ /root /root/temp LICENSE ls: cannot access /root/not.exist: Permission denied ls: cannot access not.exist: No such file or directory ls: cannot access LICENSE/: Not a directory ls: cannot access /root/temp: Permission denied LICENSE ls: cannot open directory /root: Permission denied $rls /root/not.exist not.exist LICENSE/ /root /root/temp LICENSE ls: cannot access not.exist: No such file or directory ls: cannot access LICENSE/: Not a directory ls: cannot access /root/not.exist: Permission denied ls: cannot access /root/temp: Permission denied LICENSE ls: cannot open directory /root: Permission denied
- 2h スケジューリング、環境準備
- 1h README GitHUB へ push
- 1h Ruby オプションパース、ファイル一覧取得部分調査
- 1h ls 仕様調査、ls -C 動作再現方法考察
- 1h ls -C 動作実装
- 1h ToDo 洗い出し、テストケース洗い出し
- 5h ls -C 動作再現、パス取得/展開
- 5h パス取得/展開、存在/権限チェック、エラー表示
- 2h エラー表示
- 4h オプション対応
- 1h テストスクリプト作成
- 2h -l オプション対応、対象がシンボリックリンクの場合の対応
- 1h YARD コメント追加
- 1h オプション無し時のカラム表示バグ対応とリファクタリング
- 1h テストケース洗い出し
- 2h テスト実施
- 4h テスト実施、バグ対応
- 1h README 整理
- 1h ドキュメント作成
- 1h README 整理、rls リファクタリング
- GNU coreutils ソース
- -C カラム表示部分のソース抜粋
- ファイルやパイプに繋いだ時の表示は画面表示と違う