「日次で出力される売上CSVから特定列だけを取り出して集計したい」「CSV形式のサーバーリストを読み込んで、全台に一括でヘルスチェックや設定変更を実行したい」――システム運用やデータ移行、定型バッチ処理の現場において、CSV・テキストデータの加工自動化はエンジニアにとって必須のスキルです。
bashには、標準のwhile read構文をはじめ、高速な一括配列化を行うmapfile、特定列の高速切り出しを行うcut、そして高度な条件分岐・集計が可能なawkやsedとのパイプ連携など、多彩な手法が用意されています。本記事では、「CSV・テキストデータ加工」クラスターの総合ピラー(親記事)として、基本の読み込みから配列操作、特定列抽出、実務ユースケース、ダブルクォート混在時のエスケープ落とし穴までを網羅して解説します。
1. CSV読み込み・ループ処理の基本と安全な構文
bashでCSVファイルを1行ずつ読み込んで処理する場合、もっとも基本かつ堅牢なのがwhile IFS=, read -r構文です。環境変数IFS(Internal Field Separator:内部フィールド区切り文字)にカンマを指定することで、各列の値を別々の変数へ直接代入できます。
#!/bin/bash
# --------------------------------------------------
# 基本のCSV読み込みと変数展開
# --------------------------------------------------
# サンプルCSV(users.csv)の想定構造:
# ユーザー名,メールアドレス,所属部署
# sato,sato@example.com,開発部
# suzuki,suzuki@example.com,インフラ部
csv_file="users.csv"
# IFSをカンマに設定し、read -r で安全に読み込み
while IFS=, read -r username email department
do
# 各フィールドを展開して処理(空行対策も含む)
if [[ -n "$username" ]]; then
echo "アカウント: $username | 宛先: $email | 部署: $department"
fi
done < "$csv_file"
この構文の重要なポイントは以下の3点です:
IFS=,をコマンド直前に付与: スクリプト全体のIFSを書き換えず、readコマンドの実行時のみカンマ区切りを適用するため、後続の処理に影響を与えません。-rオプションの必須指定: バックスラッシュ()をエスケープ文字として解釈せず、データそのままの文字列として読み込みます。パス名や特殊文字が含まれるデータで必須です。- 変数のダブルクォート囲み(
"$username"): 空白を含む値であっても単語分割(Word Splitting)を防ぎ、安全に扱えます。
より詳しいIFSのスコープ制御やwhile readの応用テクニックは、以下のサテライト解説記事も参考にしてください。
📖 関連詳細ガイド(サテライト記事)
2. 配列への格納(動的参照とmapfileによる一括処理)
CSVの列数が可変の場合や、行全体を一括でメモリに展開して高速走査したい場合は、配列(Array)を活用します。
① 1行を配列に分割して動的アクセス(read -a)
read -a 配列名 を指定すると、カンマで区切られた各フィールドが自動的に0始まりのインデックス配列に格納されます。
#!/bin/bash
# read -a を使った動的列アクセス
while IFS=, read -r -a fields
do
# 要素数の取得
col_count="${#fields[@]}"
# インデックス指定で参照
echo "列数: $col_count | 1列目: ${fields[0]} | 最終列: ${fields[$((col_count - 1))]}"
done < data.csv
② mapfile(readarray)による一括配列化
Bash 4.0以降で利用可能な組み込みコマンド mapfile(別名 readarray)を使うと、外部ファイルを1行ごとに配列要素として一括読み込みできます。サブシェルを起動しないため、数千行〜数万行のデータでも高速に処理できます。
#!/bin/bash
# mapfileでCSVを行単位の配列に一括格納
# -t オプションで各行末尾の改行コードを自動削除
mapfile -t lines < server_list.csv
echo "総行数: ${#lines[@]}"
# 配列をループ処理
for line in "${lines[@]}"
do
# 必要に応じてカンマで分割
IFS=, read -r host ip role <<< "$line"
echo "ホスト名: $host ($ip) - 役割: $role"
done
📖 関連詳細ガイド(サテライト記事)
3. 特定列の抽出とデータフィルタリング(cut・大量データ抽出)
「CSV全体のループは不要で、特定の2列目と4列目だけを抜き出したい」「不要なヘッダーを除去して集計用の中間ファイルを作りたい」という場合は、cutコマンドが最速の選択肢です。
| コマンド・構文 | 主な用途 | 実行例 |
|---|---|---|
cut -d, -f2 | 2列目のみを抽出 | cut -d, -f2 data.csv |
cut -d, -f1,3 | 1列目と3列目を抽出 | cut -d, -f1,3 data.csv |
cut -d, -f2- | 2列目以降の全列を抽出 | cut -d, -f2- data.csv |
cut -f1,2 | TSV(タブ区切り)の列抽出 | cut -f1,2 data.tsv |
#!/bin/bash
# cutコマンドとパイプを組み合わせた高速データ抽出
# 大規模ログCSVから「アクセス日時(1列目)」と「IPアドレス(3列目)」を抽出してユニーク集計
cut -d, -f1,3 access_log.csv | sort | uniq -c | sort -nr | head -n 10
cutは処理速度が極めて高速ですが、「ダブルクォート内のカンマ」を無視できず一律に分割してしまう制限があります。複雑な抽出条件やクォート処理が必要な場合は、後述のawkや専用抽出手法を併用します。
📖 関連詳細ガイド(サテライト記事)
4. 実務連携:awk・sed・一括置換との組み合わせ
単なるループ処理を超えて、「条件に応じた数値計算」「特定カラムの文字置換」「複数ファイルの一括編集」を行うには、awk と sed の連携が威力を発揮します。
① awkによる条件付き集計・列加工
# 金額列(3列目)が10,000円以上の行のみ抽出し、消費税込み価格を出力
awk -F, '$3 >= 10000 { printf "商品: %-15s 金額(税込): %dn", $1, $3 * 1.1 }' sales.csv
# 部署別(2列目)の合計売上(3列目)を集計
awk -F, 'NR>1 { sum[$2] += $3 } END { for (d in sum) print d, sum[d] }' sales.csv
② sedによるCSV内文字列の一括置換とファイル更新
# CSV内のドメイン名を一括更新(直接上書き保存)
sed -i 's/@old-domain.com/@new-company.jp/g' users.csv
# 複数の一括CSVファイルに対してまとめて置換を実行
find /var/data/csv/ -name "*.csv" -exec sed -i 's/STATUS_PENDING/STATUS_ACTIVE/g' {} +
📖 関連詳細ガイド(サテライト記事)
5. 実務で役立つ頻出ユースケース集(コピペ実装例)
① CSVからユーザーアカウントを一括作成・権限設定
社内システムの移行や初期構築時に役立つ、ユーザー追加スクリプトのテンプレートです。ヘッダーをスキップし、既存ユーザーの重複チェックを行いながら安全に処理します。
#!/bin/bash
set -euo pipefail
csv_file="new_users.csv"
# 1行目(ヘッダー)を飛ばして2行目から読み込み
tail -n +2 "$csv_file" | while IFS=, read -r username group initial_pass
do
# 空白行はスキップ
[[ -z "$username" ]] && continue
if id "$username" &>/dev/null; then
echo "[SKIP] ユーザー $username は既に存在します。"
else
useradd -m -g "$group" -s /bin/bash "$username"
echo "${username}:${initial_pass}" | chpasswd
echo "[OK] ユーザー $username を作成しました(グループ: $group)"
fi
done
② サーバーリストCSVに基づくSSH並行ヘルスチェック
対象サーバーのホスト名とIPアドレスが記載されたCSVを順次読み込み、SSH経由でディスク使用率やロードアベレージを点検します。
#!/bin/bash
# サーバー一覧CSV(ホスト名,IPアドレス,環境)から稼働確認
csv_file="servers.csv"
while IFS=, read -r hostname ip_addr env_type
do
# ヘッダー行判定
[[ "$hostname" == "hostname" ]] && continue
echo "=== Checking: $hostname ($ip_addr) [$env_type] ==="
# 接続タイムアウト3秒でuptimeを取得
if ssh -o ConnectTimeout=3 -o BatchMode=yes "admin@${ip_addr}" "uptime" 2>/dev/null; then
echo "[$hostname] 正常稼働中"
else
echo "[$hostname] 警告: 接続できませんでした" >&2
fi
done < "$csv_file"
③ Excelファイル(xlsx)からCSVへの自動変換パイプライン
実務ではクライアントや管理部門からExcel形式でデータが提供されることが多々あります。Linuxサーバー上ではssconvert(Gnumericツール)やlibreofficeのCLIを使ってヘッドレス変換してからbashスクリプトへパイプで渡すのが定石です。
# ssconvert を使って高速にCSV変換
ssconvert input_data.xlsx output_data.csv
# ヘッドレスLibreOfficeによるバッチ変換
libreoffice --headless --convert-to csv --outdir /tmp/ input_data.xlsx
# 変換後のCSVを即座にパイプでbash処理
tail -n +2 /tmp/input_data.csv | while IFS=, read -r col1 col2 col3
do
echo "処理中: $col1 -> $col2"
done
6. 実務での注意点・落とし穴とトラブルシューティング
① ダブルクォート囲み・カンマ混在データ(”Tokyo, Japan”,100)の罠
標準のIFS=, read構文は「単純なカンマ区切り」のみを認識するため、"東京都, 新宿区",160-0022 のようにダブルクォート内にカンマを含むCSVを渡すと、誤って列が分割されてしまいます。
- 対処法1(awk FPAT): GNU awkの
FPATを使い、awk -v FPAT='([^,]+)|("[^"]+")'でクォート内を保護して処理する。 - 対処法2(専用ツール活用): CSV専用CLIツール
csvkit(csvcut,csvgrep)や Pythonのcsvモジュールワンライナーに委譲する。
# Pythonのワンライナーでクォート付きCSVを安全にTSV変換してbashへ渡す例
python3 -c '
import csv, sys
reader = csv.reader(sys.stdin)
for row in reader:
print("t".join(row))
' < quoted_data.csv | while IFS=$'t' read -r col1 col2 col3
do
echo "安全取得: $col1 | $col2 | $col3"
done
② 改行コード(CRLF vs LF)の混入
Windows環境で作成・編集されたCSVファイルには行末に rn(CRLF)が含まれています。このままbashで読み込むと、最終列の変数の末尾に不可視の r(キャリッジリターン)が残り、文字列比較の失敗や出力崩れの原因になります。
# dos2unix で事前に改行コードをLFに変換
dos2unix data.csv
# またはスクリプト内で tr を通してCRを除去
tr -d 'r' < data.csv | while IFS=, read -r col1 col2
do
# 正常に比較・処理可能
if [[ "$col2" == "active" ]]; then
echo "$col1 is active"
fi
done
③ ヘッダー行の安全なスキップ手法
1行目のヘッダー(列名)を処理対象外にするには、主に2つの方法があります:
- パイプライン方式:
tail -n +2 file.csv | while IFS=, read -r ...(外部プロセスで2行目以降に絞る) - ループ内空打ち方式: ループの直前に
read -r headerを1回実行してファイルポインタを進める
{
# 1行目(ヘッダー)を読み捨て
read -r header
# 2行目以降を通常処理
while IFS=, read -r col1 col2
do
echo "$col1: $col2"
done
} < data.csv
7. まとめ:CSV・テキスト加工手法の早見表
| 加工手法・ツール | 得意なユースケース | メリット | 注意点・デメリット |
|---|---|---|---|
| while IFS=, read -r | 1行ごとの逐次処理・外部コマンド実行 | Bash標準・依存なし・シンプル | 行数が多いと速度低下、クォート内カンマに非対応 |
| mapfile (readarray) | 全行の一括配列化・高速ループ | サブシェル不要で高速・配列操作が容易 | 巨大ファイルではメモリを消費(Bash 4+必須) |
| cut コマンド | 特定列の高速切り出し | 最速の処理速度・パイプ連携に最適 | カンマ以外の柔軟な条件判定やクォート処理は不可 |
| awk コマンド | 条件フィルタリング・集計・計算 | 柔軟なプログラミング構文・高速 | 文法がスクリプト言語的で習熟が必要 |
| sed コマンド | 文字列の一括置換・行削除・ファイル更新 | 正規表現を用いた高速テキスト一括編集 | 列単位の高度な計算には不向き |
日々の定型業務では、「シンプルな列抽出は cut / awk、1行ずつの自動化バッチは while read、全行メモリ走査は mapfile」 と使い分けるのが最も効率的です。それぞれの詳細なコマンドオプションや実務レシピは、本記事で紹介した各サテライト記事もぜひブックマークして活用してください。

コメント