dirlens 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,13 +8,13 @@
8
8
  ## 出力例
9
9
 
10
10
  ```
11
- Desktop/ (3.74 MB)
12
- ├── EmptyDir/ (0 bytes)
13
- ├── Project/ (712 KB)
14
- │ ├── assets/ (512 KB)
15
- │ │ └── images/ (512 KB)
11
+ Desktop/ (2 dirs, 2 files, 3.74 MB)
12
+ ├── EmptyDir/ (0 dirs, 0 files, 0 bytes)
13
+ ├── Project/ (2 dirs, 1 file, 712 KB)
14
+ │ ├── assets/ (1 dir, 0 files, 512 KB)
15
+ │ │ └── images/ (0 dirs, 1 file, 512 KB)
16
16
  │ │ └── logo.png (512 KB)
17
- │ ├── src/ (80 KB)
17
+ │ ├── src/ (0 dirs, 1 file, 80 KB)
18
18
  │ │ └── util.py (80 KB)
19
19
  │ └── main.py (120 KB)
20
20
  ├── archive.zip (3 MB)
@@ -31,7 +31,8 @@ Desktop/ (3.74 MB)
31
31
  - **カラー表示** — ディレクトリ・ファイル・シンボリックリンクを色で識別
32
32
  - **自動サイズ変換** — bytes / KB / MB / GB / TB
33
33
  - **ディレクトリサイズ** — サブディレクトリの合計サイズを自動計算
34
- - **隠しファイル対応**`-a` で表示切り替え
34
+ - **アイテム数表示**各ディレクトリの直下にある dirs / files 数を表示
35
+ - **隠しファイル対応** — `-a` で表示切り替え(アイテム数にも反映)
35
36
  - **サイズ順ソート** — `-s` で大きいものから表示
36
37
 
37
38
  ---
@@ -62,16 +63,16 @@ npm uninstall -g dirlens
62
63
 
63
64
  ### macOS / Linux(スクリプト直接インストール)
64
65
 
65
- ```bash
66
- # 実行権限を付与
67
- chmod +x dirlens
66
+ GitHubリポジトリから `dirlens.py` をダウンロードして使用します。
68
67
 
68
+ ```bash
69
69
  # /usr/local/bin にインストール(どこからでも呼べるようになる)
70
- sudo cp dirlens /usr/local/bin/
70
+ sudo install -m 755 dirlens.py /usr/local/bin/dirlens
71
71
 
72
72
  # ── または sudo なしでユーザーローカルにインストール ──
73
73
  mkdir -p ~/.local/bin
74
- cp dirlens ~/.local/bin/
74
+ cp dirlens.py ~/.local/bin/dirlens
75
+ chmod +x ~/.local/bin/dirlens
75
76
 
76
77
  # ~/.zshrc(zsh)または ~/.bashrc(bash)に以下を追記:
77
78
  export PATH="$HOME/.local/bin:$PATH"
@@ -89,7 +90,7 @@ dirlens --help
89
90
 
90
91
  ### Windows(スクリプト直接インストール)
91
92
 
92
- 1. `dirlens` **`dirlens.py`** に改名して任意のフォルダへ置く
93
+ 1. `dirlens.py` `dirlens.bat` を任意のフォルダへ置く
93
94
  (例: `C:\Users\ユーザー名\bin\`)
94
95
 
95
96
  2. 同じフォルダに **`dirlens.bat`** を置く(同梱のものを使用):
@@ -178,3 +179,4 @@ dirlens --no-color > tree.txt
178
179
  - **シンボリックリンク先のディレクトリ**は展開せず `→` マークで表示
179
180
  - 権限がないディレクトリは `[アクセス拒否]` と表示してスキップ
180
181
  - 非常に深いディレクトリ(1万階層以上)は `-d` で深さを制限してください
182
+ - **ホームフォルダ(`~/`)やルート(`/`)で実行すると固まる場合があります** — サイズ計算は `-d` の表示制限に関わらず底まで全再帰するため、`~/Library` や iCloud Drive など大容量・ネットワークマウントのディレクトリで時間がかかります。プロジェクトフォルダなど範囲を絞って実行してください
package/bin/dirlens CHANGED
File without changes
package/dirlens.py CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- dirlens – ファイルサイズ付きディレクトリツリー表示ツール
3
+ dirlens – ファイルサイズ+アイテム数付きディレクトリツリー表示ツール
4
4
  対応環境: macOS / Linux / Windows (Python 3.8+)
5
5
  """
6
6
 
@@ -14,7 +14,6 @@ def _enable_color():
14
14
  if not hasattr(sys.stdout, "isatty") or not sys.stdout.isatty():
15
15
  return False
16
16
  if os.name == "nt":
17
- # Windows: VT100モードを有効にする
18
17
  try:
19
18
  import ctypes
20
19
  kernel32 = ctypes.windll.kernel32
@@ -22,8 +21,8 @@ def _enable_color():
22
21
  except Exception:
23
22
  pass
24
23
  return bool(
25
- os.environ.get("WT_SESSION") # Windows Terminal
26
- or os.environ.get("TERM_PROGRAM") # VS Code 等
24
+ os.environ.get("WT_SESSION")
25
+ or os.environ.get("TERM_PROGRAM")
27
26
  or os.environ.get("TERM")
28
27
  or os.environ.get("ANSICON")
29
28
  )
@@ -40,12 +39,10 @@ GREEN = "\033[32m"
40
39
  MAGENTA = "\033[35m"
41
40
 
42
41
  def c(text, *codes):
43
- """ANSIカラーを適用する。カラー無効時はそのまま返す。"""
44
42
  return ("".join(codes) + text + RESET) if USE_COLOR else text
45
43
 
46
44
  # ─── サイズ表示 ───────────────────────────────────────────────
47
45
  def fmt_size(n):
48
- """バイト数を人が読みやすい文字列に変換する。"""
49
46
  if n == 0:
50
47
  return "0 bytes"
51
48
  for unit, factor in (("TB", 1 << 40), ("GB", 1 << 30), ("MB", 1 << 20), ("KB", 1 << 10)):
@@ -58,7 +55,6 @@ def fmt_size(n):
58
55
  _cache = {}
59
56
 
60
57
  def dir_size(path):
61
- """ディレクトリ以下の合計バイト数を再帰的に計算する(シンボリックリンクは追わない)。"""
62
58
  if path in _cache:
63
59
  return _cache[path]
64
60
  total = 0
@@ -77,6 +73,25 @@ def dir_size(path):
77
73
  _cache[path] = total
78
74
  return total
79
75
 
76
+ # ─── アイテム数カウント ────────────────────────────────────────
77
+ def count_items(path, show_all):
78
+ """ディレクトリ直下のアイテム数 (num_dirs, num_files) を返す。"""
79
+ try:
80
+ entries = list(os.scandir(path))
81
+ except OSError:
82
+ return (0, 0)
83
+ if not show_all:
84
+ entries = [e for e in entries if not e.name.startswith(".")]
85
+ nd = sum(1 for e in entries if e.is_dir(follow_symlinks=False))
86
+ nf = sum(1 for e in entries if not e.is_dir(follow_symlinks=False))
87
+ return (nd, nf)
88
+
89
+ def fmt_meta(nd, nf, sz):
90
+ """アイテム数+サイズをまとめて文字列化する。"""
91
+ d_str = f"{nd} {'dir' if nd == 1 else 'dirs'}"
92
+ f_str = f"{nf} {'file' if nf == 1 else 'files'}"
93
+ return f"({d_str}, {f_str}, {fmt_size(sz)})"
94
+
80
95
  # ─── ツリー描画 ───────────────────────────────────────────────
81
96
  PIPE = "│ "
82
97
  FORK = "├── "
@@ -84,7 +99,6 @@ LAST = "└── "
84
99
  BLANK = " "
85
100
 
86
101
  def render(path, prefix, depth, max_depth, show_all, by_size, stats):
87
- """ディレクトリ内容を再帰的にツリー表示する。"""
88
102
  if max_depth is not None and depth >= max_depth:
89
103
  return
90
104
 
@@ -94,10 +108,7 @@ def render(path, prefix, depth, max_depth, show_all, by_size, stats):
94
108
  print(f"{prefix}{LAST}{c('[アクセス拒否]', DIM)}")
95
109
  return
96
110
 
97
- # 隠しファイルのフィルタ(オプション依存)
98
111
  entries = [e for e in raw if show_all or not e.name.startswith(".")]
99
-
100
- # ディレクトリ(シンボリックリンク除く)とそれ以外(ファイル+シンボリックリンク)に分類
101
112
  dirs = [e for e in entries if e.is_dir(follow_symlinks=False)]
102
113
  files = [e for e in entries if not e.is_dir(follow_symlinks=False)]
103
114
 
@@ -107,7 +118,6 @@ def render(path, prefix, depth, max_depth, show_all, by_size, stats):
107
118
  except OSError:
108
119
  return 0
109
120
 
110
- # ソート:名前順 or サイズ順
111
121
  if by_size:
112
122
  dirs.sort(key=lambda e: dir_size(e.path), reverse=True)
113
123
  files.sort(key=lambda e: entry_size(e), reverse=True)
@@ -115,7 +125,6 @@ def render(path, prefix, depth, max_depth, show_all, by_size, stats):
115
125
  dirs.sort(key=lambda e: e.name.casefold())
116
126
  files.sort(key=lambda e: e.name.casefold())
117
127
 
118
- # ディレクトリを先に、次にファイル
119
128
  combined = dirs + files
120
129
 
121
130
  for i, entry in enumerate(combined):
@@ -124,11 +133,12 @@ def render(path, prefix, depth, max_depth, show_all, by_size, stats):
124
133
  cont = BLANK if is_last else PIPE
125
134
 
126
135
  if entry.is_dir(follow_symlinks=False):
127
- sz = dir_size(entry.path)
136
+ sz = dir_size(entry.path)
137
+ nd, nf = count_items(entry.path, show_all)
128
138
  stats["dirs"] += 1
129
139
  name = c(f"{entry.name}/", BOLD, CYAN)
130
- size = c(f"({fmt_size(sz)})", DIM)
131
- print(f"{prefix}{branch}{name} {size}")
140
+ meta = c(fmt_meta(nd, nf, sz), DIM)
141
+ print(f"{prefix}{branch}{name} {meta}")
132
142
  render(entry.path, prefix + cont, depth + 1, max_depth, show_all, by_size, stats)
133
143
  else:
134
144
  sz = entry_size(entry)
@@ -145,7 +155,7 @@ def main():
145
155
 
146
156
  ap = argparse.ArgumentParser(
147
157
  prog="dirlens",
148
- description="ファイルサイズ付きのディレクトリツリーを表示します",
158
+ description="ファイルサイズ+アイテム数付きのディレクトリツリーを表示します",
149
159
  formatter_class=argparse.RawDescriptionHelpFormatter,
150
160
  epilog=(
151
161
  "使用例:\n"
@@ -175,13 +185,13 @@ def main():
175
185
  print(f"エラー: '{args.path}' はディレクトリではありません", file=sys.stderr)
176
186
  sys.exit(1)
177
187
 
178
- # ドライブルート(Windows の C:\ 等)対応
179
188
  root_label = target.name if target.name else str(target)
189
+ root_sz = dir_size(str(target))
190
+ nd, nf = count_items(str(target), args.all)
180
191
 
181
- root_sz = dir_size(str(target))
182
192
  root_name = c(f"{root_label}/", BOLD, BLUE)
183
- root_size = c(f"({fmt_size(root_sz)})", DIM)
184
- print(f"{root_name} {root_size}")
193
+ root_meta = c(fmt_meta(nd, nf, root_sz), DIM)
194
+ print(f"{root_name} {root_meta}")
185
195
 
186
196
  stats = {"files": 0, "dirs": 0}
187
197
  render(str(target), "", 0, args.depth, args.all, args.sort_size, stats)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dirlens",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Directory tree viewer with file sizes / ファイルサイズ付きディレクトリツリー表示ツール",
5
5
  "bin": {
6
6
  "dirlens": "./bin/dirlens"