4.0K 2.1M 512B $ 【awk】
treeでファイルサイズを後ろに表示する

ディレクトリ構造を一覧で確認したいとき、私はよくtreeコマンドを使います。
構造が一目で分かり、とても便利です。

ただ、以前から一つだけ気になっていた点がありました。
ファイルサイズを表示すると、必ずファイル名の前に出てしまうことです。

一覧として眺めるときは、「名前 → サイズ」の順で目に入ったほうが、私は読みやすく感じます。

そこで今回は、treeでサイズを表示しつつ表示位置をファイル名の後ろに移動し、watchで1秒ごとに更新表示するコマンドを作りました1

<svg class="eyecatch-svg" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg">
  <!-- 背景 -->
  <rect width="192" height="192" fill="white" rx="32" ry="32"/>
  
  <!-- ツリー構造の線 -->
  <g stroke="#607D8B" stroke-width="3" stroke-linecap="round" fill="none">
    <!-- 縦の主軸 -->
    <line x1="48" y1="56" x2="48" y2="152"/>
    
    <!-- 分岐線(第1階層) -->
    <path d="M 48 72 L 68 72"/>
    <path d="M 48 104 L 68 104"/>
    <path d="M 48 136 L 68 136"/>
    
    <!-- 分岐線(第2階層) -->
    <line x1="68" y1="72" x2="68" y2="104"/>
    <path d="M 68 88 L 88 88"/>
  </g>
  
  <!-- ファイルアイコン群 -->
  <g>
    <!-- ファイル1 -->
    <g transform="translate(88, 68)">
      <rect width="28" height="36" rx="2" fill="#2196F3"/>
      <path d="M 16 0 L 16 8 L 28 8 L 28 36 L 0 36 L 0 8 L 16 8 Z" fill="#1976D2"/>
      <polygon points="16,0 28,8 16,8" fill="#90CAF9"/>
    </g>
    
    <!-- ファイル2(サブ) -->
    <g transform="translate(108, 80)">
      <rect width="22" height="28" rx="2" fill="#4CAF50"/>
      <path d="M 12 0 L 12 6 L 22 6 L 22 28 L 0 28 L 0 6 L 12 6 Z" fill="#388E3C"/>
      <polygon points="12,0 22,6 12,6" fill="#A5D6A7"/>
    </g>
    
    <!-- ファイル3 -->
    <g transform="translate(88, 96)">
      <rect width="28" height="36" rx="2" fill="#FF9800"/>
      <path d="M 16 0 L 16 8 L 28 8 L 28 36 L 0 36 L 0 8 L 16 8 Z" fill="#F57C00"/>
      <polygon points="16,0 28,8 16,8" fill="#FFB74D"/>
    </g>
    
    <!-- ファイル4 -->
    <g transform="translate(88, 128)">
      <rect width="28" height="36" rx="2" fill="#9C27B0"/>
      <path d="M 16 0 L 16 8 L 28 8 L 28 36 L 0 36 L 0 8 L 16 8 Z" fill="#7B1FA2"/>
      <polygon points="16,0 28,8 16,8" fill="#CE93D8"/>
    </g>
  </g>
  
  <!-- サイズ表示ラベル -->
  <g>
    <!-- ラベル1 -->
    <g transform="translate(126, 86)">
      <rect width="32" height="14" rx="3" fill="#E0E0E0"/>
      <text x="16" y="10" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#607D8B" text-anchor="middle">4.0K</text>
    </g>
    
    <!-- ラベル2 -->
    <g transform="translate(126, 114)">
      <rect width="32" height="14" rx="3" fill="#E0E0E0"/>
      <text x="16" y="10" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#607D8B" text-anchor="middle">2.1M</text>
    </g>
    
    <!-- ラベル3 -->
    <g transform="translate(126, 146)">
      <rect width="32" height="14" rx="3" fill="#E0E0E0"/>
      <text x="16" y="10" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#607D8B" text-anchor="middle">512B</text>
    </g>
  </g>
  
  <!-- ターミナルプロンプト記号(アクセント) -->
  <g transform="translate(24, 32)">
    <text font-family="monospace" font-size="16" font-weight="bold" fill="#2196F3">$</text>
  </g>
</svg>【awk】<br class="chiilabo-br is-on"> treeでファイルサイズを後ろに表示する
watch -n 1 -d "tree -sh | awk 'match(\$0,/\\[[[:space:]]*[0-9.]+([BKMGTP]|[KMGTPE]iB)?\\]/){s=substr(\$0,RSTART,RLENGTH);gsub(/\\[[[:space:]]*[0-9.]+([BKMGTP]|[KMGTPE]iB)?\\][[:space:]]*/,\" \");sub(/[[:space:]]+$/,\"\");print \$0\" \"s;next}{print}'"Code language: JavaScript (javascript)

-dは差分のハイライト表示です2

関連記事

1. treeでサイズを表示するとどうなるか

1.1. tree -sh を試す

まずは基本です。

treeコマンドの基本 tree -sh -s ファイルサイズを表示 -h 人間が読みやすい単位(KB/MB) ├── [4.0K] file.txt └── [2.1M] data.log
tree -sh

-sはサイズ表示、-hは人間向け(KやM単位)です3

出力は次のようになります。

├── [4.0K] file.txtCode language: CSS (css)

完成形は次のコマンドです。

これをワンライナーにすると、

出力はこうなります。

├── README.md [   3]
└── docs [4.0K]
    └── hybrid_chatbot_specification.md [4.1K]Code language: CSS (css)

サイズが角括弧付きで表示されますが、必ず前に出ます。
オプションで位置を変える方法はありません。

この時点で、「tree単体では無理そうだ」と判断しました。

2. awkで表示を加工する

treeの出力はただのテキストです。
ならば、一度サイズを取り除き、行末に付け直せばいいと考えました。

awkでテキスト加工 1 パターン検索 match() 2 サイズ抽出 substr() 3 位置変更 gsub() 主要な関数: • match() – [数字 単位] パターンを検索 • RSTART/RLENGTH – 位置と長さを取得

テキスト加工といえばawkです。

tree -sh |
awk '
  match($0, /\[[[:space:]]*[0-9.]+([BKMGTP]|[KMGTPE]iB)?\]/) {
    s = substr($0, RSTART, RLENGTH)

    # サイズ表記(+直後の余分な空白)を削除して、行末に付け直す
    gsub(/\[[[:space:]]*[0-9.]+([BKMGTP]|[KMGTPE]iB)?\][[:space:]]*/, " ")
    sub(/[[:space:]]+$/, "")

    print $0 " " s
    next
  }

  { print }
'
Code language: PHP (php)

行のどこかに[数字 単位]という形があれば、それをサイズとして扱う方法です。

awkのmatch()関数を使うと、文字列の中から特定のパターンを探せます。
これで行中のサイズも拾えるようになりました4

最終的に考えた条件はこうです。

  • [の後に空白があってもよい。
  • 数字は必須。
  • 単位はあってもなくてもよい。

この条件を正規表現に落とし込みました。
ここは少し読みにくいですが、「現実の出力に合わせて妥協した」部分です。

正規表現パターン /\[[[:space:]]*[0-9.]+([BKMGTP]|…)\]/ \[ 開始記号 [:space:]* 空白(任意) [0-9.]+ 数字と小数点 単位 KB/MB/GB等 マッチ例:[4.0K] [2.1M] [512B]

名前を読んでからサイズが目に入る。
個人的には、これだけでかなり見やすくなりました。

  1. watchコマンドは指定されたコマンドを定期的に実行し、出力をリアルタイムで表示するLinuxユーティリティです。デフォルトでは2秒ごとに更新されますが、-nオプションで間隔をカスタマイズできます – watch command in Linux with Examples – GeeksforGeeks
  2. watchコマンドの-dオプションは、連続する更新間の差分を強調表示し、変更箇所を見つけやすくします – How to Use the Watch Command in Linux: Monitor Commands in Real Time
  3. -sオプションはファイルサイズをバイト単位で表示し、-hオプションと組み合わせることで、KB、MB、GBなどの人間が読みやすい単位で表示されます – Linux ‘tree Command’ Usage Examples for Beginners
  4. match()関数とともにRSTARTおよびRLENGTH変数を使用することで、一致した部分文字列の位置と長さを特定し、substr()関数で抽出できます – RLENGTH and RSTART – Learning AWK Programming