導入
ここまでの道具を組み合わせて、「ディレクトリの中のファイルを一括処理する」という、実務そのものの作業を書いてみましょう。ループ・条件分岐・変数が同時に動く感覚をつかみます。
説明
やることは「files の中の .txt ファイルを1つずつ見て、行数を数え、合計を出す」です。
#!/bin/bash
TARGET_DIR=files
total=0
echo "=== $TARGET_DIR の .txt を集計します ==="
for file in "$TARGET_DIR"/*.txt; do
# ファイルが無い場合、パターンがそのまま残るので確認する
if [ ! -f "$file" ]; then
continue
fi
lines=$(wc -l < "$file")
echo "$file: $lines 行"
total=$((total + lines))
done
echo "合計: $total 行"
新しい要素はありません。第2章の変数と $( )、第4章の if、この章の for を組み合わせただけです。スクリプトは、小さな道具の組み合わせでできている——これがシェルの世界観です。
注意点を2つ。
"$TARGET_DIR"/*.txtのように、変数は囲み、*は囲まない。囲んでしまうと展開されず、files/*.txtという名前のファイルを探しに行きます。- 一致するファイルが1つも無いとき、
forはパターンの文字列そのもの(files/*.txt)で1回だけ回ります。だから中で[ -f "$file" ]を確認するのが安全です。
flowchart LR
A["files/*.txt を展開"] --> B["a.txt / b.txt / c.txt"]
B --> C["1つずつ wc -l で行数"]
C --> D["total に足し込む"]
D --> E["合計を表示"]
やってみよう
スクリプトを実行して、3つのファイルの行数と合計が出ることを確かめましょう。次に *.txt を * に変えると memo.md も対象になり、合計が変わります。さらに TARGET_DIR=logs にすれば、まったく別のディレクトリを同じスクリプトで集計できます——変数にしておいた甲斐がここに出ます。
演習
TARGET_DIR を logs に変え、*.txt を *.log にして実行し、ログファイルの行数を集計してください(合計は18行になります)。
ヒント1を見る
TARGET_DIR=logs と for file in "$TARGET_DIR"/*.log の2か所を直します。
ヒント2を見る
logs には access.log(10行)と app.log(8行)があります。