【Awk & Jq活用講座】CSVデータ編集で使える最低限覚えておきたい正規表現の活用法
はじめに
AwkとJqの正規表現
EXP
操作 | Awk | Jq |
---|---|---|
任意の1文字にマッチ | /./ | "." |
任意の文字列に最長マッチ | /.*/ | ".*" |
任意の文字列に最短マッチ | /.*?/ | ".*?" |
行の先頭にマッチ | /^EXP/ | "^EXP" |
行の末尾にマッチ | /EXP$/ | "EXP$" |
[...]内のどれか1文字にマッチ(右の例は0~9の数字とa~zの小文字) | /[0-9a-z]/ | "[0-9a-z]" |
[...]以外の1文字にマッチ(右の例は0~9の数字とa~zの小文字以外) | /[^0-9a-z]/ | "[^0-9a-z]" |
文字エスケープ | /\EXP/ | "\\EXP" |
任意の数字1文字にマッチ | /\d/ | "\\d" |
数字以外の1文字にマッチ | /\D/ | "\\D" |
アルファベットか数字かアンダーバー文字の1文字にマッチ | /\w/ | "\\w" |
アルファベットか数字かアンダーバー文字以外の1文字にマッチ | /\W/ | "\\W" |
空白文字(改行やタブも含む)の1文字にマッチ | /\s/ | "\\s" |
空白文字以外の1文字にマッチ | /\S/ | "\\S" |
パターンをグループ化(丸括弧で囲う) | /(EXP)/ | "(EXP)" |
どれかのパターンにマッチ(右の例はabcかefのパターンどちらか) | /abc|ef/ | "abc|ef" |
前の文字の0個または1個にマッチ | /EXP?/ | "EXP?" |
前の文字の0個以上にマッチ | /EXP*/ | "EXP*" |
前の文字の1個以上にマッチ | /EXP+/ | "EXP+" |
前の文字のm個にマッチ | /EXP{m}/ | "EXP{m}" |
(...)の中の文字列がm回繰り返したときにマッチ | /(EXP){m}/ | "(EXP){m}" |
前の文字のm個以上n個以下にマッチ | /EXP{m,n}/ | "EXP{m,n}" |
前の文字のm個以上にマッチ | /EXP{m,}/ | "EXP{m,}" |
前の文字のn個以下にマッチ | /EXP{,n}/ | "EXP{,n}" |
/
\
\/
\
\\
Awkの場合
1. gensub関数で利用
2. Awk内のブロック条件内で利用
3. if条件の中で利用
1. gensub関数
$ 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 '
#👇一列目が数字で終わるもの
$1 ~ /[0-9]$/ {
print FNR ": " $1
}
' << EOF
1234
abc
dfgh
567
ij
EOF
#👇出力
1: 1234
4: 567
~
3. 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
Jqの場合
<文字列> | フィルター関数(<正規表現>)
<文字列> | フィルター関数(<正規表現>; <フラグ>)
<文字列> | フィルター関数([<正規表現>, <正規表現>, ...])
<文字列> | フィルター関数([<正規表現>, ..., <正規表現>, <フラグ>])
g - グローバル検索:
正規表現に一致した全てのパターンを探す
i - 大文字と小文字を区別:
指定すると大文字と小文字を区別しない
m - マルチラインモード:
改行文字にも'.'でマッチ出来るようになる。
先頭位置を^、最後尾位置を$で指定できる。
s - シングルラインモード:
デフォルト。
改行文字までを検索する。
ただし先頭位置は'^' -> '\A'、最後尾位置は'$' -> '\Z'として利用可能。
n - 空マッチ:
指定すると空のマッチ結果の場合を無視する
p - 自動ラインモード:
sとmを自動で切り替え
l - 最長マッチモード:
デフォルトを最長マッチで検索するようになる。
x - 拡張正規モード:
拡張正規が利用できる。
空白文字の無視、コメントのスキップなど
+ split
+ test
+ match
+ capture
+ gsub
split関数
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関数
$ 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"
まとめ
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。