|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "id": "a2d8d7c3", |
| 6 | + "metadata": {}, |
| 7 | + "source": [ |
| 8 | + "# Bash Shellを用いた10行目の抽出問題\n", |
| 9 | + "\n", |
| 10 | + "この問題では、テキストファイルの10行目だけを出力する方法を解説します。3つの異なるアプローチと、それぞれの動作原理を図解付きで説明します。" |
| 11 | + ] |
| 12 | + }, |
| 13 | + { |
| 14 | + "cell_type": "code", |
| 15 | + "execution_count": null, |
| 16 | + "id": "896f0cc3", |
| 17 | + "metadata": { |
| 18 | + "vscode": { |
| 19 | + "languageId": "shellscript" |
| 20 | + } |
| 21 | + }, |
| 22 | + "outputs": [], |
| 23 | + "source": [ |
| 24 | + "#!/bin/bash\n", |
| 25 | + "\n", |
| 26 | + "# ========================================\n", |
| 27 | + "# 解法1: sed を使用する方法\n", |
| 28 | + "# ========================================\n", |
| 29 | + "# Analyze Complexity\n", |
| 30 | + "# Runtime 23 ms\n", |
| 31 | + "# Beats 69.41%\n", |
| 32 | + "# Memory 3.85 MB\n", |
| 33 | + "# Beats 91.20%\n", |
| 34 | + "\n", |
| 35 | + "echo \"=== 解法1: sed ===\"\n", |
| 36 | + "sed -n '10p' file.txt\n", |
| 37 | + "\n", |
| 38 | + "# 解説:\n", |
| 39 | + "# -n : デフォルトの出力を抑制\n", |
| 40 | + "# '10p' : 10行目のみを出力(print)\n", |
| 41 | + "\n", |
| 42 | + "\n", |
| 43 | + "# ========================================\n", |
| 44 | + "# 解法2: head と tail を組み合わせる方法\n", |
| 45 | + "# ========================================\n", |
| 46 | + "# Analyze Complexity\n", |
| 47 | + "# Runtime 27 ms\n", |
| 48 | + "# Beats 24.40%\n", |
| 49 | + "# Memory 3.88 MB\n", |
| 50 | + "# Beats 91.20%\n", |
| 51 | + "\n", |
| 52 | + "echo -e \"\\n=== 解法2: head + tail ===\"\n", |
| 53 | + "tail -n +10 file.txt | head -n 1\n", |
| 54 | + "\n", |
| 55 | + "# 解説:\n", |
| 56 | + "# head -n 10 : 最初の10行を取得\n", |
| 57 | + "# tail -n 1 : その中から最後の1行(=10行目)を取得\n", |
| 58 | + "\n", |
| 59 | + "\n", |
| 60 | + "# ========================================\n", |
| 61 | + "# 解法3: awk を使用する方法\n", |
| 62 | + "# ========================================\n", |
| 63 | + "# Analyze Complexity\n", |
| 64 | + "# Runtime 30 ms\n", |
| 65 | + "# Beats 7.19%\n", |
| 66 | + "# Memory 3.92 MB\n", |
| 67 | + "# Beats 52.52%\n", |
| 68 | + "\n", |
| 69 | + "echo -e \"\\n=== 解法3: awk ===\"\n", |
| 70 | + "awk 'NR==10' file.txt\n", |
| 71 | + "\n", |
| 72 | + "# 解説:\n", |
| 73 | + "# NR : 現在の行番号(Number of Records)\n", |
| 74 | + "# NR==10 : 行番号が10の時にその行を出力\n", |
| 75 | + "\n", |
| 76 | + "\n", |
| 77 | + "# ========================================\n", |
| 78 | + "# 補足: 10行未満の場合の処理\n", |
| 79 | + "# ========================================\n", |
| 80 | + "# Analyze Complexity\n", |
| 81 | + "# Runtime 26 ms\n", |
| 82 | + "# Beats 34.48%\n", |
| 83 | + "# Memory 3.94 MB\n", |
| 84 | + "# Beats 52.52%\n", |
| 85 | + "\n", |
| 86 | + "echo -e \"\\n=== 10行未満のファイルへの対応 ===\"\n", |
| 87 | + "\n", |
| 88 | + "# エラーチェック付きバージョン\n", |
| 89 | + "if [ $(wc -l < file.txt) -ge 10 ]; then\n", |
| 90 | + " sed -n '10p' file.txt\n", |
| 91 | + "else\n", |
| 92 | + " echo \"\"\n", |
| 93 | + "fi" |
| 94 | + ] |
| 95 | + }, |
| 96 | + { |
| 97 | + "cell_type": "markdown", |
| 98 | + "id": "b7bf7ad3", |
| 99 | + "metadata": {}, |
| 100 | + "source": [ |
| 101 | + "## 詳細な図解と解説\n", |
| 102 | + "\n", |
| 103 | + "### **解法1: `sed` を使用する方法**\n", |
| 104 | + "\n", |
| 105 | + "```bash\n", |
| 106 | + "sed -n '10p' file.txt\n", |
| 107 | + "```\n", |
| 108 | + "\n", |
| 109 | + "**動作原理の図解:**\n", |
| 110 | + "\n", |
| 111 | + "```\n", |
| 112 | + "file.txt sed -n '10p' 出力\n", |
| 113 | + "┌─────────┐ ┌──────────────┐ ┌──────────┐\n", |
| 114 | + "│ Line 1 │──┐ │ │ │ │\n", |
| 115 | + "│ Line 2 │ │ │ -n: 自動出力 │ │ │\n", |
| 116 | + "│ Line 3 │ │ │ OFF │ │ │\n", |
| 117 | + "│ Line 4 │ ├──→│ │────────→│ │\n", |
| 118 | + "│ Line 5 │ │ │ 10p: 10行目 │ │ │\n", |
| 119 | + "│ Line 6 │ │ │ のみ出力 │ │ │\n", |
| 120 | + "│ Line 7 │ │ │ │ │ │\n", |
| 121 | + "│ Line 8 │ │ │ │ │ │\n", |
| 122 | + "│ Line 9 │ │ │ │ │ │\n", |
| 123 | + "│ Line 10 │──┘ │ ✓ │ │ Line 10 │\n", |
| 124 | + "└─────────┘ └──────────────┘ └──────────┘\n", |
| 125 | + "```\n", |
| 126 | + "\n", |
| 127 | + "**特徴:**\n", |
| 128 | + "- `-n`: デフォルトの出力を抑制\n", |
| 129 | + "- `10p`: 10行目に到達したら出力(print)\n", |
| 130 | + "- **最もシンプルで効率的**\n", |
| 131 | + "\n", |
| 132 | + "---\n", |
| 133 | + "\n", |
| 134 | + "### **解法2: `head` と `tail` の組み合わせ**\n", |
| 135 | + "\n", |
| 136 | + "```bash\n", |
| 137 | + "head -n 10 file.txt | tail -n 1\n", |
| 138 | + "```\n", |
| 139 | + "\n", |
| 140 | + "**動作原理の図解:**\n", |
| 141 | + "\n", |
| 142 | + "```\n", |
| 143 | + "file.txt head -n 10 tail -n 1 出力\n", |
| 144 | + "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", |
| 145 | + "│ Line 1 │ │ Line 1 │ │ │ │ │\n", |
| 146 | + "│ Line 2 │ │ Line 2 │ │ │ │ │\n", |
| 147 | + "│ Line 3 │ │ Line 3 │ │ │ │ │\n", |
| 148 | + "│ Line 4 │───→│ Line 4 │ │ │ │ │\n", |
| 149 | + "│ Line 5 │ │ Line 5 │ │ │ │ │\n", |
| 150 | + "│ Line 6 │ │ Line 6 │──────→│ 最後の1行 │───→│ Line 10 │\n", |
| 151 | + "│ Line 7 │ │ Line 7 │ │ を取得 │ │ │\n", |
| 152 | + "│ Line 8 │ │ Line 8 │ │ │ │ │\n", |
| 153 | + "│ Line 9 │ │ Line 9 │ │ ✓ │ │ │\n", |
| 154 | + "│ Line 10 │ │ Line 10 │ │ Line 10 │ │ │\n", |
| 155 | + "│ Line 11 │ └──────────┘ └──────────┘ └──────────┘\n", |
| 156 | + "│ Line 12 │ ↑ ここまで\n", |
| 157 | + "└─────────┘\n", |
| 158 | + "```\n", |
| 159 | + "\n", |
| 160 | + "**処理の流れ:**\n", |
| 161 | + "1. `head -n 10`: 最初の10行を抽出\n", |
| 162 | + "2. `tail -n 1`: その中から最後の1行(つまり元の10行目)を取得\n", |
| 163 | + "\n", |
| 164 | + "**特徴:**\n", |
| 165 | + "- 直感的で理解しやすい\n", |
| 166 | + "- 2つのコマンドを組み合わせるため、やや冗長\n", |
| 167 | + "\n", |
| 168 | + "---\n", |
| 169 | + "\n", |
| 170 | + "### **解法3: `awk` を使用する方法**\n", |
| 171 | + "\n", |
| 172 | + "```bash\n", |
| 173 | + "awk 'NR==10' file.txt\n", |
| 174 | + "```\n", |
| 175 | + "\n", |
| 176 | + "**動作原理の図解:**\n", |
| 177 | + "\n", |
| 178 | + "```\n", |
| 179 | + "file.txt awk 'NR==10' 出力\n", |
| 180 | + "┌─────────┐ ┌───────────────────┐ ┌──────────┐\n", |
| 181 | + "│ Line 1 │ NR=1 ──→ │ NR==10? No │ │ │\n", |
| 182 | + "│ Line 2 │ NR=2 ──→ │ NR==10? No │ │ │\n", |
| 183 | + "│ Line 3 │ NR=3 ──→ │ NR==10? No │ │ │\n", |
| 184 | + "│ Line 4 │ NR=4 ──→ │ NR==10? No │ │ │\n", |
| 185 | + "│ Line 5 │ NR=5 ──→ │ NR==10? No │────────→│ │\n", |
| 186 | + "│ Line 6 │ NR=6 ──→ │ NR==10? No │ │ │\n", |
| 187 | + "│ Line 7 │ NR=7 ──→ │ NR==10? No │ │ │\n", |
| 188 | + "│ Line 8 │ NR=8 ──→ │ NR==10? No │ │ │\n", |
| 189 | + "│ Line 9 │ NR=9 ──→ │ NR==10? No │ │ │\n", |
| 190 | + "│ Line 10 │ NR=10 ──→ │ NR==10? Yes! ✓ │ │ Line 10 │\n", |
| 191 | + "└─────────┘ └───────────────────┘ └──────────┘\n", |
| 192 | + "```\n", |
| 193 | + "\n", |
| 194 | + "**特徴:**\n", |
| 195 | + "- `NR` (Number of Records): 現在の行番号を保持する変数\n", |
| 196 | + "- 条件が真の時のみ行を出力(デフォルト動作)\n", |
| 197 | + "- **テキスト処理に強力で柔軟性が高い**\n", |
| 198 | + "\n", |
| 199 | + "---\n", |
| 200 | + "\n", |
| 201 | + "## **10行未満のファイルへの対応**\n", |
| 202 | + "\n", |
| 203 | + "ファイルが10行未満の場合の動作比較:\n", |
| 204 | + "\n", |
| 205 | + "```\n", |
| 206 | + "5行のファイルの場合:\n", |
| 207 | + "\n", |
| 208 | + "┌─────────┐\n", |
| 209 | + "│ Line 1 │\n", |
| 210 | + "│ Line 2 │\n", |
| 211 | + "│ Line 3 │ ← 5行しかない\n", |
| 212 | + "│ Line 4 │\n", |
| 213 | + "│ Line 5 │\n", |
| 214 | + "└─────────┘\n", |
| 215 | + "\n", |
| 216 | + "sed -n '10p' → 何も出力しない\n", |
| 217 | + "head -n 10 | tail -n 1 → Line 5 を出力\n", |
| 218 | + "awk 'NR==10' → 何も出力しない\n", |
| 219 | + "```\n", |
| 220 | + "\n", |
| 221 | + "**エラーチェック付きバージョン:**\n", |
| 222 | + "\n", |
| 223 | + "```bash\n", |
| 224 | + "# 行数を確認してから処理\n", |
| 225 | + "if [ $(wc -l < file.txt) -ge 10 ]; then\n", |
| 226 | + " sed -n '10p' file.txt\n", |
| 227 | + "else\n", |
| 228 | + " echo \"エラー: ファイルが10行未満です\" >&2\n", |
| 229 | + "fi\n", |
| 230 | + "```\n", |
| 231 | + "\n", |
| 232 | + "---\n", |
| 233 | + "\n", |
| 234 | + "## **各解法の比較表**\n", |
| 235 | + "\n", |
| 236 | + "| 解法 | コマンド | 長所 | 短所 | 10行未満の動作 |\n", |
| 237 | + "|------|----------|------|------|----------------|\n", |
| 238 | + "| **sed** | `sed -n '10p'` | シンプル、高速 | sed文法の知識が必要 | 何も出力しない |\n", |
| 239 | + "| **head+tail** | `head -n 10 \\| tail -n 1` | 直感的 | 2つのプロセスが必要 | 最終行を出力 |\n", |
| 240 | + "| **awk** | `awk 'NR==10'` | 柔軟性が高い | やや複雑 | 何も出力しない |\n", |
| 241 | + "\n", |
| 242 | + "---\n", |
| 243 | + "\n", |
| 244 | + "## **推奨される解法**\n", |
| 245 | + "\n", |
| 246 | + "**最もシンプルで効率的:** `sed -n '10p' file.txt`\n", |
| 247 | + "\n", |
| 248 | + "これが最も一般的で、多くの場合に推奨される方法です!\n", |
| 249 | + "\n", |
| 250 | + "ご指摘ありがとうございます!問題を特定しました。\n", |
| 251 | + "\n", |
| 252 | + "## **問題点の分析**\n", |
| 253 | + "\n", |
| 254 | + "ファイルが**9行しかない**場合、`head -n 10`は9行すべてを出力し、`tail -n 1`はその最後の行(9行目)を出力してしまいます。\n", |
| 255 | + "\n", |
| 256 | + "### **動作の図解**\n", |
| 257 | + "\n", |
| 258 | + "```\n", |
| 259 | + "file.txt (9行) head -n 10 tail -n 1 出力\n", |
| 260 | + "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", |
| 261 | + "│ 1 │ │ 1 │ │ │ │ │\n", |
| 262 | + "│ 2 │ │ 2 │ │ │ │ │\n", |
| 263 | + "│ 3 │ │ 3 │ │ │ │ │\n", |
| 264 | + "│ 4 │──────→ 4 │ │ │ │ │\n", |
| 265 | + "│ 5 │ │ 5 │──────→ 最後の1行 │────→ 9 │\n", |
| 266 | + "│ 6 │ │ 6 │ │ を取得 │ │ │\n", |
| 267 | + "│ 7 │ │ 7 │ │ ✓ │ │ ✗ 誤り! │\n", |
| 268 | + "│ 8 │ │ 8 │ │ 9 │ │ │\n", |
| 269 | + "│ 9 │ │ 9 │ │ │ │ │\n", |
| 270 | + "└─────────┘ └──────────┘ └──────────┘ └──────────┘\n", |
| 271 | + " 9行しかない → 9行出力 → 9を出力\n", |
| 272 | + "\n", |
| 273 | + "期待される出力: 何も出力しない(10行目は存在しない)\n", |
| 274 | + "実際の出力: 9\n", |
| 275 | + "```\n", |
| 276 | + "\n", |
| 277 | + "## **正しい解法**## **3つの正しい解法**\n", |
| 278 | + "\n", |
| 279 | + "### **解法1: `sed -n '10p'`(最も推奨)**\n", |
| 280 | + "\n", |
| 281 | + "```bash\n", |
| 282 | + "sed -n '10p' file.txt\n", |
| 283 | + "```\n", |
| 284 | + "\n", |
| 285 | + "✅ **正しい動作:**\n", |
| 286 | + "- 10行ある場合: 10行目を出力\n", |
| 287 | + "- 9行しかない場合: 何も出力しない\n", |
| 288 | + "\n", |
| 289 | + "---\n", |
| 290 | + "\n", |
| 291 | + "### **解法2: `awk 'NR==10'`(推奨)**\n", |
| 292 | + "\n", |
| 293 | + "```bash\n", |
| 294 | + "awk 'NR==10' file.txt\n", |
| 295 | + "```\n", |
| 296 | + "\n", |
| 297 | + "✅ **正しい動作:**\n", |
| 298 | + "- 10行ある場合: 10行目を出力\n", |
| 299 | + "- 9行しかない場合: 何も出力しない\n", |
| 300 | + "\n", |
| 301 | + "---\n", |
| 302 | + "\n", |
| 303 | + "### **解法3: `tail -n +10 | head -n 1`(推奨)**\n", |
| 304 | + "\n", |
| 305 | + "```bash\n", |
| 306 | + "tail -n +10 file.txt | head -n 1\n", |
| 307 | + "```\n", |
| 308 | + "\n", |
| 309 | + "**動作原理の図解:**\n", |
| 310 | + "\n", |
| 311 | + "```\n", |
| 312 | + "file.txt (9行) tail -n +10 head -n 1 出力\n", |
| 313 | + "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", |
| 314 | + "│ 1 │ │ │ │ │ │ │\n", |
| 315 | + "│ 2 │ │ │ │ │ │ │\n", |
| 316 | + "│ 3 │ │ │ │ │ │ │\n", |
| 317 | + "│ 4 │ │ │ │ │ │ │\n", |
| 318 | + "│ 5 │ │ (空) │──────→ (空) │────→ (空) │\n", |
| 319 | + "│ 6 │ │ │ │ │ │ │\n", |
| 320 | + "│ 7 │ │ │ │ │ │ │\n", |
| 321 | + "│ 8 │ │ │ │ │ │ │\n", |
| 322 | + "│ 9 │ │ │ │ │ │ │\n", |
| 323 | + "└─────────┘ └──────────┘ └──────────┘ └──────────┘\n", |
| 324 | + " 10行目以降がない → 何も出力しない ✓\n", |
| 325 | + "\n", |
| 326 | + "\n", |
| 327 | + "file.txt (10行) tail -n +10 head -n 1 出力\n", |
| 328 | + "┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐\n", |
| 329 | + "│ 1 │ │ │ │ │ │ │\n", |
| 330 | + "│ 2 │ │ │ │ │ │ │\n", |
| 331 | + "│ 3 │ │ │ │ ✓ │ │ │\n", |
| 332 | + "│ 4 │ │ 10 │──────→ 10 │────→ 10 │\n", |
| 333 | + "│ 5 │ │ │ │ │ │ │\n", |
| 334 | + "│ 6 │ │ │ │ │ │ │\n", |
| 335 | + "│ 7 │ │ │ │ │ │ │\n", |
| 336 | + "│ 8 │ │ │ │ │ │ │\n", |
| 337 | + "│ 9 │ │ │ │ │ │ │\n", |
| 338 | + "│ 10 │ │ │ │ │ │ │\n", |
| 339 | + "└─────────┘ └──────────┘ └──────────┘ └──────────┘\n", |
| 340 | + "```\n", |
| 341 | + "\n", |
| 342 | + "✅ **正しい動作:**\n", |
| 343 | + "- `tail -n +10`: 10行目から最後まで取得(+10は「10行目から」の意味)\n", |
| 344 | + "- `head -n 1`: その最初の1行を取得\n", |
| 345 | + "\n", |
| 346 | + "---\n", |
| 347 | + "\n", |
| 348 | + "## **なぜ `head -n 10 | tail -n 1` は間違いなのか**\n", |
| 349 | + "\n", |
| 350 | + "```\n", |
| 351 | + "❌ 間違った解法の動作:\n", |
| 352 | + "\n", |
| 353 | + "9行のファイル:\n", |
| 354 | + "1. head -n 10 → 9行すべてを出力(10行ないので9行しか取れない)\n", |
| 355 | + "2. tail -n 1 → その最後の1行 = 9行目を出力\n", |
| 356 | + "結果: 9 が出力される(誤り)\n", |
| 357 | + "\n", |
| 358 | + "10行のファイル:\n", |
| 359 | + "1. head -n 10 → 10行を出力\n", |
| 360 | + "2. tail -n 1 → その最後の1行 = 10行目を出力\n", |
| 361 | + "結果: 10 が出力される(正しい)\n", |
| 362 | + "```\n", |
| 363 | + "\n", |
| 364 | + "## **正解のまとめ**\n", |
| 365 | + "\n", |
| 366 | + "LeetCode/オンラインジャッジで正解する解法:\n", |
| 367 | + "\n", |
| 368 | + "```bash\n", |
| 369 | + "# 解法1(最もシンプル)\n", |
| 370 | + "sed -n '10p' file.txt\n", |
| 371 | + "\n", |
| 372 | + "# 解法2(汎用性が高い)\n", |
| 373 | + "awk 'NR==10' file.txt\n", |
| 374 | + "\n", |
| 375 | + "# 解法3(tailの+記法を使用)\n", |
| 376 | + "tail -n +10 file.txt | head -n 1\n", |
| 377 | + "```\n", |
| 378 | + "\n", |
| 379 | + "これらはすべて、ファイルが10行未満の場合は**何も出力しない**ため、テストケースをすべてパスします!" |
| 380 | + ] |
| 381 | + } |
| 382 | + ], |
| 383 | + "metadata": { |
| 384 | + "language_info": { |
| 385 | + "name": "python" |
| 386 | + } |
| 387 | + }, |
| 388 | + "nbformat": 4, |
| 389 | + "nbformat_minor": 5 |
| 390 | +} |
0 commit comments