Table of Contents
問題設定

このような形で,カレントディレクトリ下に存在するフォルダのみを一覧として取得したい.
カレントディレクトリの構成
1
2
3
4
5
% ls
s1/   s13/  s17/  s3/  s7/         text11.txt  text15.txt  text19.txt  text4.txt  text8.txt
s10/  s14/  s18/  s4/  s8/         text12.txt  text16.txt  text1.txt   text5.txt  text9.txt
s11/  s15/  s19/  s5/  s9/         text13.txt  text17.txt  text2.txt   text6.txt
s12/  s16/  s2/   s6/  text10.txt  text14.txt  text18.txt  text3.txt   text7.txt
ls commandで試してみる
マニュアルを確認してみる
まず基本通りmanコマンドをたたきoptionが存在する確認してみた.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
% man ls
LS(1)                                  User Commands                                 LS(1)
NAME
       ls - list directory contents
SYNOPSIS
       ls [OPTION]... [FILE]...
DESCRIPTION
       List  information about the FILEs (the current directory by default).  Sort entries
       alphabetically if none of -cftuvSUX nor --sort is specified.
       Mandatory arguments to long options are mandatory for short options too.
       
       <略>
       -d, --directory
              list directories themselves, not their contents
こたえが-dを指定するだけで求めているものが出力されるとの仮説を立てて実行したところ
1
2
% ls -d
./
とフォルダがカレントディレクトリに存在するにもかかわらず期待した結果を返さない. find ./ -maxdepth 1 -type d | wc -l
なぜls -dが意図した挙動にならないのか?
lsに何も引数を与えない場合,デフォルトではカレントディレクトリに対してコマンドが実行されます.
通常,これはディレクトリの内容をリストアップすることを意味しますが,-dを指定したことで内容は参照されることなく,ディレクトリ自体のリスト=カレントディレクトリのみの情報が取得されることになります.
カレントディレクトリのみの情報が取得されていることの確認は,ls -ldの出力結果から確認できるフォルダ作成日を確認するとわかります.
ls -ld */とパターンを指定するといける
ファイルとディレクトリの違いの一つとして, */というパターンに後者はマッチするが前者はマッチしないという特徴があります. これを活用して,中身を参照することなく*/にマッチするものだけで出力すればまずもとめていたものを出力することができます.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 % ls -ld */
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s1//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s10//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s11//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s12//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s13//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s14//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s15//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s16//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s17//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s18//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s19//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s2//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s3//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s4//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s5//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s6//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s7//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s8//
drwxr-xr-- 2 unko_unko unko_unko 4096 Aug  4 01:44 s9//
余分な/も出力されてしまいますが一応求めていたものを得ることができました.
なお,-dを指定せず実行してしまうとsubdirectoryの中身も参照してしまいフォルダに所属するフォルダまで出力されてしまいます.
-dはデレクトリのみを出力するのではなくて, not their contents=(中身を確認しないですよ)と理解するほうが正確といえます.
grepと組合せて余計な/を消す
*/というパターンに後者はマッチするのはディレクトリのみなので,grepコマンドを使って出力するという解決策もあり得ます.
1
% ls -l | GREP_COLOR="1;34" grep -E "\s\S+?/$"
Then,

- GREP_COLOR="1;34"はgrepでマッチした箇所を青色で表示させるコマンドです
- "\s\S+?/$"の意味は- /で終了, spaceと一回以上連続するspace以外の文字の最小組合せの意味です
Table of Contents
フォルダサイズが4096 byteと表示されていて変
フォルダの中にファイルが存在しており,フォルダサイズは以下のように表示されるべきところ,すべて4096と表示されています.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
% du -shx */
260K	s2/
100K	s14/
56K	s1/
44K	s4/
4.0K	s9/
4.0K	s8/
4.0K	s7/
4.0K	s6/
4.0K	s5/
4.0K	s3/<div style='border-radius: 1em; border-style:solid; border-color:#D3D3D3; background-color:#F8F8F8'>
<p class="h4">  Table of Contents</p>
<!-- START doctoc -->
<!-- END doctoc -->
</div>
4.0K	s19/
4.0K	s18/
4.0K	s17/
4.0K	s16/
4.0K	s15/
4.0K	s13/
4.0K	s12/
4.0K	s11/
4.0K	s10/
ls -lで出力されるサイズはそのオブジェクトのスペース使用量を表示するのみで,フォルダ下に存在するファイルサイズを合計した値は出力されません. 直感的なイメージとしてはそのオブジェクトのデータブロックサイズを返しているという感じです.
また,ls -lで表示されるtimeもオブジェクトが作成された時間を返しており,個人的にはここは最終更新時間が欲しい.
要件再定義
- ls -lライクに以下の情報を出力したい:- フォルダの種類と許可属性
- ハードリンク数
- 所有者とグループ
- ディスク使用量
- 最終更新タイムスタンプ
- カレントディレクトリ下に存在するフォルダ名称
 
- フォルダの色は青色で表示する
- インデントはできる限り揃える
出力結果イメージ

実装
最終的に.zshrcに書き込んだ関数
1
2
3
4
5
6
7
8
function ls-dir(){
  join -1 4 -2 9  <(du -shx --time */) <(ls -Flh)\
  |awk '{print $5, $6, $7, $8, $2, $3, $4, $1}' FS=' '\
  |column -t\
  |GREP_COLOR="1;34" grep -E "\s\S+?/$"
}
alias ls-dir=ls-dir
作成方針
- lsと- duコマンドを用いて必要情報を取得する
- 上記結果をフォルダ名をキーとして,joinコマンドを用いてINNER JOINする
- awkとcolumnで表示形式を整える
- grepで色付け<div style='border-radius: 1em; border-style:solid; border-color:#D3D3D3; background-color:#F8F8F8'>
Table of Contents
</div>
disk usageデータの取得:duコマンド
duコマンドは, 指定したファイルのサイズやディレクトリのディスクの使用量を表示するコマンドです.
Syntax
1
% du [OPTIONS] PATH
- PATHの部分はワイルドカード検索も可能
- ディレクトリ一覧のディスク使用量を検索したい場合は */と指定関連ポスト
duコマンドの主なオプション<div style='border-radius: 1em; border-style:solid; border-color:#D3D3D3; background-color:#F8F8F8'>
Table of Contents
</div>
| 短いオプション | 長いオプション | 説明 | 
|---|---|---|
| -s | --summarize | 指定したディレクトリの合計のみを表示する(サブディレクトリの行が表示されなくなる) | 
| -x | --one-file-system | 異なるファイルシステム(パーティション)にあるディレクトリをスキップする | 
| -h | --human-readable | サイズに応じて読みやすい単位で表示する | 
動作確認
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 % du -shx */
56K	s1/
4.0K	s10/
4.0K	s11/
4.0K	s12/
4.0K	s13/
100K	s14/
4.0K	s15/
4.0K	s16/<div style='border-radius: 1em; border-style:solid; border-color:#D3D3D3; background-color:#F8F8F8'>
<p class="h4">  Table of Contents</p>
<!-- START doctoc -->
<!-- END doctoc -->
</div>
4.0K	s17/
4.0K	s18/
4.0K	s19/
これでフォルダごとのDisk Usageの情報を取得.
joinコマンドを用いてlsとduの出力結果を結合する
Syntax: joinコマンド
1
join [OPTION] -1 FIELD -2 FIELD FILE1 FILE2
Table of Contents
- -1 FIELD: FILE1のjoin key fieldの指定
- -2 FIELD: FILE2のjoin key fieldの指定
- デフォルトの結合フィールドは空白で区切られた最初カラム
columnコマンドを用いた表整形
columnコマンドは,複数列があるデータを左揃えで表示させることができます.
Option
| -c カラム数 | 表示の幅を指定します。 | 
| -t | 入力行のカラム数を自動判定し、表を作成します。 | 
| -s | 入力行を列(カラム)に分ける区切り文字をしています.-t オプションと併用します.デフォルトは半角スペース | 
| -x | 行を埋める前に列を埋めます | 
Appendix
lsコマンドのカラー設定
ls 実行時のファイルの配色は環境変数 $LS_COLORSで設定されています. ファイルの配色は
- ファイルの種類 (ディレクトリ, シンボリックリンクなど)
- 拡張子 (.jpg , .gz など)
の2つで決まっており以下のコマンドで確かめることができます.
1
2
% echo $LS_COLORS 
rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:(略)
色と色番号とファイルの種類
| 白色 | 37 | 通常ファイル | 
| 青色 | 34 | ディレクトリ | 
| 水色 | 36 | シンボリックリンク | 
| 赤色 | 31 | 圧縮ファイル | 
| 緑色 | 32 | 実行ファイル | 
| 黄色 | 33 | 特殊ファイル | 
ディレクトリのハードリンク
ディレクトリの場合はusrやhomeといった名前の他に自分自身を示関連ポストす.というハードリンクが自動で作成されます. 従って,ディレクトリにはハードリンクが「最低2つある」ことになります(= 新規に作成したディレクトリに所属したオブジェクト数はからディレクトリであっても 2と表示される).
サブディレクトリがある場合は、サブディレクトリから見た..(親ディレクトリ)があるため、ハードリンクの数は3つとなります. つまり.特別にハードリンクを増やしていない限り,ディレクトリのハードリンク数は「サブディレクトリの数+2」となります。
Def: ハードリンク
ファイルやディレクトリに別名をつけ, 異なった名前で同じデータにアクセスできる仕組みの1つをハードリンクといいます.
ファイルの実体(= ストレージ上に保存されているデータ)が1つだけであっても, その実体を指し表す複数のファイルがあっても, それぞれのファイルの内容と属性はハードリンクでは同一です.
一般的にはシンボリックリンクが使用されます. 違いとしては, シンボ関連ポストリックリンクはリンク先を呼び出すだけなので, それ自体を削除しても本体には影響がありませんが, 本体を削除してしまうとリンクが切れてしまいます. 一方, ハードリンクは両方とも同じものとみなされるので, 異なるファイルが同じデータ実体を参照している形になります. そのため, どちらかを削除してももう片方は引き続きデータを参照することができるというメリットがあります.
ハードリンクのiノード番号の確認
Linuxでは, iノードにファイルの属性情報が格納されます. 新規にファイルやディレクトリを作成するとファイルシステム上で重複しないiノード番号が割り当てられます. この番号の確認方法は
1
% ls -i
で確認することができます. 空ディレクトリに存在する.と..のiノード番号をls -iで確認すると, それぞれが自分自身と親ディレクトリのiノード番号と一致することが確認できます.
ハードリンクファイルの作成方法: lnコマンド
関連ポスト
lnコマンド(LiNKコマンド)は, ファイルやディレクトリに対するリンクを作るコマンドです.
1
% ln <original file> <link file>
ただし, ディレクトリのハードリンクはユーザーからは作成できません. 「hard link not allowed for directory」というメッセージが出てきてしまいます. シンボリックリンクは作成可能です.
file pathは絶対パスで指定することを推奨します.
References
- Ryo’s Tech Blog > Linux commandの復習: awkコマンド
- Ubuntuサーバー徹底入門, 中島 能和 著
- StackExchange > Listing directories and understanding ls
- StackExchange > du only for directories
(注意:GitHub Accountが必要となります)