カテゴリー
【Awk & Jq活用講座】CSVデータ編集で使える最低限覚えておきたい正規表現の活用法
※ 当ページには【広告/PR】を含む場合があります。
2021/03/26
AwkやSedといったテキストを編集する代表的なコマンドをより高度に操作するためには、
とはいえ各コマンドには正規表現の作法に違いが有りますし、同じコマンドでも違うOSで動いている場合にも動作に違いがあることもあります。
今回はあまりマニアックな正規表現のテクニックなどは避けながら、一般的なAwk(GNU Awk)とJqでcsvファイルを操作する際に最低限覚えておきたい正規表現をまとめてみます。
はじめに
当サイトではオフィス業務のComputer-Aidedなハイブリッドな方法を模索し、より効率的なExcel業務を実現したい多忙なオフィスワーカー向けの主にAwkとSedを使うシェル講座です。
シェルスクリプトはどこでもどんなOSでも基本的に使えて、しかも一度使い方を覚えると、Excelと組み合わせて最高に効率の良いオフィスワークツールが作れることでしょう。

AwkとJqの正規表現
基本的な操作に対する正規表現の対応表は以下のようになります。 (※ただし正規表現内の
EXP
だいたいこの表にあるもので事足りるように思います。
AwkとJqの正規表現の大きな違いは、Awkの場合には正規表現をスラッシュで囲った中身が対象になるのに対し、Jqではダブルクオーテーション括弧の中身が検索されます。
よって、Awkでの
/
\
\/
逆にJqではバックスラッシュ
\
\\
Awkの場合
おそらくcsvデータを集計・成形する際にAwkで正規表現を利用するパターンは主に下のユースケースが多いと思います。
1. gensub関数で利用
2. Awk内のブロック条件内で利用
3. if条件の中で利用
それぞれのユースケースを以下でざっと見ていきましょう。
1. gensub関数
gensubはGnu awkに収録されている関数ですのでそれ以外のawkでは利用できませんが、パターンマッチなどが利用でき非常に強力なユーティリティ関数です。 後方参照をさせるときに利用します。
$ awk -F"," 'BEGIN {OFS=","} {
date = gensub(/[0-9]{4}\/([0-9]{2})\/([0-9]{2})/, "\\1月\\2日", "g", $1);
print date, $2, $3;
}' << EOF
2021/04/06,出張,山田
2021/04/08,会議,佐藤
2021/04/17,リモートワーク,鈴木
EOF
#👇出力
04月06日,出張,山田
04月08日,会議,佐藤
04月17日,リモートワーク,鈴木
2. Awk内のブロック条件内で利用
おそらくAwkの正規表現の利用法でもっとも多いのがアクションブロック前にパターン条件を指定して使う時の方法では無いかと思います。
$ awk '
#👇一列目が数字で終わるもの
$1 ~ /[0-9]$/ {
print FNR ": " $1
}
' << EOF
1234
abc
dfgh
567
ij
EOF
#👇出力
1: 1234
4: 567
正規表現を比較するには
~
3. If条件の中で利用
先ほどと同じですが、if構文の中でも正規表現で比較する
~
$ echo 'abc,efg123,hijk,lmn,456,ABC' | awk -F"," '{
for (i=1;i<=NF;i++) {
#👇3つの数字の連続の列があったら表示
if ($i ~ /\d{3}/) {
print $i;
}
}
}'
#👇出力
efg123
456
$ echo 'abc,efg123,hijk,lmn,456,ABC' | awk -F"," '{
for (i=1;i<=NF;i++) {
#👇3つの数字の連続以外の列があったら表示
if ($i !~ /\d{3}/) {
print $i;
}
}
}'
#👇出力
abc
hijk
lmn
ABC
なおAwkでは、セルの中の文字列を適切に処理する用途が多いので、さほど難解な正規表現は利用しないほうがベターです。 もし、テキストファイル全体に及ぶような編集が必要であればSedでテキストの前処理を行いましょう。
Jqの場合
Jqの正規表現もAwkと考え方は一緒ですが、jsonオプジェクトに変換した後に、適切にフィールドの値を正規表現で操作するようなことが主流の使い方です。
つまりは、リファレンスでも述べられいるように文字列を後処理する関数フィルターを指定するようなユースパターンとして利用できます。
<文字列> | フィルター関数(<正規表現>)
<文字列> | フィルター関数(<正規表現>; <フラグ>)
<文字列> | フィルター関数([<正規表現>, <正規表現>, ...])
<文字列> | フィルター関数([<正規表現>, ..., <正規表現>, <フラグ>])
指定出来るフラグに関しては
g - グローバル検索:
正規表現に一致した全てのパターンを探す
i - 大文字と小文字を区別:
指定すると大文字と小文字を区別しない
m - マルチラインモード:
改行文字にも'.'でマッチ出来るようになる。
先頭位置を^、最後尾位置を$で指定できる。
s - シングルラインモード:
デフォルト。
改行文字までを検索する。
ただし先頭位置は'^' -> '\A'、最後尾位置は'$' -> '\Z'として利用可能。
n - 空マッチ:
指定すると空のマッチ結果の場合を無視する
p - 自動ラインモード:
sとmを自動で切り替え
l - 最長マッチモード:
デフォルトを最長マッチで検索するようになる。
x - 拡張正規モード:
拡張正規が利用できる。
空白文字の無視、コメントのスキップなど
ということで正規表現の利用できる関数を使うことになるのですが、
+ split
+ test
+ match
+ capture
+ gsub
を使えればcsvデータを扱うには十分事足りると思います。
ここでは良く利用するsplitとtestに着目し、これに絞って利用例をあげていきます。
split関数
split関数は、本題のようにcsvデータにとっては、コンマ切りしてjsonオブジェクトに切り分けるテクニックに欠かせない関数の一つです。
以下はこの一連の記事で毎回出てくる利用法です。
jq -sR '
#👇mapを使うために配列化のために([...])でラップする
[
#👇改行位置で配列に変換し、中身を取り出す
split("\\r?\\n"; "g")[]
#👇空文字を弾く
| select(length > 0)
#👇コンマ切りで配列に変換する
| split(","; "g")
]' << EOF
2021/04/06,出張,山田
2021/04/08,会議,佐藤
2021/04/17,リモートワーク,鈴木
EOF
#👇出力
[
[
"2021/04/06",
"出張",
"山田"
],
[
"2021/04/08",
"会議",
"佐藤"
],
[
"2021/04/17",
"リモートワーク",
"鈴木"
]
]
test関数
Jqでのtest関数はよくselectと組み合わせることで、Json配列から条件を満たす要素を取り出す操作に利用できます。
$ echo '["abc", "efg", "123"]' | jq -s '.[][] | select(. | test("[0-9]{3}"))'
#👇出力
"123"
#👇testは結果をtrue/falseで返すのでnotを追加すると否定のフィルターとなる
$ echo '["abc", "efg", "123"]' | jq -s '.[][] | select(. | test("[0-9]{3}") | not)'
#👇出力
"abc"
"efg"
まとめ
以上、AwkとJqでの正規表現の基本的な利用方法と主要なユースケースの紹介でした。
さらにcsvデータの有効活用に磨きをかけるためにはパターンマッチングや後方参照のテクニックを理解する必要がありますが、Awkでのgensub関数や、Jqでのcapture関数の応用方法はまた別の機会にまとめて紹介したいと思います。
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー