カテゴリー
【Awk & Jq活用講座】 文字列の分割を分割を極める ~ split関数の使い方
※ 当ページには【広告/PR】を含む場合があります。
2021/04/01
CSV形式のデータとは、その名の通りコンマ文字(,)を区切り位置として利用してデータの境界を区切るのテキストのことです。
ですが世の中でデータを表現するのはCSVファイルばかりではなく、CSVではない区切り位置のルールを持ったデータ形式のテキストファイルも多く存在しています。
今回は任意の区切り文字(セパレーター)を設定し、任意の文字をより柔軟・簡単に分割(split)する方法を解説していきます。
はじめに
当サイトではオフィス業務のComputer-Aidedなハイブリッドな方法を模索し、より効率的なExcel業務を実現したい多忙なオフィスワーカー向けの主にAwkとSedを使うシェル講座です。
シェルスクリプトはどこでもどんなOSでも基本的に使えて、しかも一度使い方を覚えると、Excelと組み合わせて最高に効率の良いオフィスワークツールが作れることでしょう。

データファイルのsplit(分割)の基礎
今回のお題として、CSV以外のコンマ切り文字を持った形式のテキストを入力として扱ってみます。
なお最終的な出力はCSV形式で統一します。
区切り文字(セパレーター)としてはどのような文字でも基本的に利用できますが、今回の例では空白文字(スペース文字)とアンパサンド文字(&)の2つの場合を取り扱ってみます。
以下のようなコマンドでテストデータを予め生成しておきます。
$ cat << EOF > testdata.ssv
山下モゲ雄 営業部 本社 3年
島田フガ子 経理部 名古屋支部 15年
岡田ピポ太 製造部 山口工場 8年
沢口モフ代 人事部 本社 4年
銭形ガメ吉 海外部 メキシコ支部 11年
EOF
$ cat << EOF > testdata.asv
山下モゲ雄&営業部&本社&3年
島田フガ子&経理部&名古屋支部&15年
岡田ピポ太&製造部&山口工場&8年
沢口モフ代&人事部&本社&4年
銭形ガメ吉&海外部&メキシコ支部&11年
EOF
Awkでsplitする場合
まずはAwkのパターンからやってみます。
Awkではオプション
-F
-F","
#👇区切り文字がスペース文字の場合には-F" "は省略可
$ awk -F" " 'BEGIN{ OFS="," } {
print $1,$2,$3,$4;
}' testdata.ssv
#👇Csvで出力
山下モゲ雄,営業部,本社,3年
島田フガ子,経理部,名古屋支部,15年
岡田ピポ太,製造部,山口工場,8年
沢口モフ代,人事部,本社,4年
銭形ガメ吉,海外部,メキシコ支部,11年
$ awk -F"&" 'BEGIN{ OFS="," } {
print $1,$2,$3,$4;
}' testdata.asv
#👇Csvで出力
山下モゲ雄,営業部,本社,3年
島田フガ子,経理部,名古屋支部,15年
岡田ピポ太,製造部,山口工場,8年
沢口モフ代,人事部,本社,4年
銭形ガメ吉,海外部,メキシコ支部,11年
ということで区切り文字の柔軟な変更はAwkのもっとも得意とする処理の一つと言えます。
ちなみにオプション引数を使いたくない場合には、Awk内のBEGINのアクションブロックなどで
FS
$ awk '
BEGIN {
FS=" "; OFS=",";
} {
print $1,$2,$3,$4;
}' testdata.ssv
#👇Csvで出力
山下モゲ雄,営業部,本社,3年
島田フガ子,経理部,名古屋支部,15年
岡田ピポ太,製造部,山口工場,8年
沢口モフ代,人事部,本社,4年
銭形ガメ吉,海外部,メキシコ支部,11年
$ awk '
BEGIN {
FS="&"; OFS=",";
} {
print $1,$2,$3,$4;
}' testdata.asv
#👇Csvで出力
山下モゲ雄,営業部,本社,3年
島田フガ子,経理部,名古屋支部,15年
岡田ピポ太,製造部,山口工場,8年
沢口モフ代,人事部,本社,4年
銭形ガメ吉,海外部,メキシコ支部,11年
複雑なセパレーターの設定
オプション
-F
FS
例えば以下のようにカスタマイズ可能です。
$ awk '
BEGIN {
FS="[ ,+&$-/]"; OFS=",";
} {
print $1,$2,$3,$4;
}' << EOF
山下モゲ雄+営業部&本社/3年
島田フガ子,経理部-名古屋支部,15年
岡田ピポ太,製造部-山口工場,8年
沢口モフ代$人事部 本社&4年
銭形ガメ吉/海外部$メキシコ支部+11年
EOF
#👇Csvで出力
山下モゲ雄,営業部,本社,3年
島田フガ子,経理部,名古屋支部,15年
岡田ピポ太,製造部,山口工場,8年
沢口モフ代,人事部,本社,4年
銭形ガメ吉,海外部,メキシコ支部,11年
同時に複数のセパレーターを指定する場合、文字の順序によってはAwkに受付られないものが有り、エラーが発生する場合があります。 エラーが発生した場合は、その都度順序を調整してみてください。
$ awk '
BEGIN {
FS="[ ,+&-$/]"; OFS=",";
} {
print $1,$2,$3,$4;
}' << EOF
山下モゲ雄+営業部&本社/3年
島田フガ子,経理部-名古屋支部,15年
岡田ピポ太,製造部-山口工場,8年
沢口モフ代$人事部 本社&4年
銭形ガメ吉/海外部$メキシコ支部+11年
EOF
#👇Csvで出力
awk: bad regex '[ ,+&-$/]': Invalid character range
Jqでsplitする場合
では先程Awkでやってみた分解をJqでも行ってみます。
JqでのSplit系のメソッドは何通りかありますが、使用に注意が必要なのは正規表現が引数として扱える扱えないの区別があることです。
split(文字列):
セパレーター(区切り位置)となる文字列で分割した文字列を配列として返す。
引数の文字列は正規表現ではないので注意
split(パターン; フラグ):
上のsplit(文字列)メソッドの正規表現拡張版。
フラグを指定することでこちらの関数が識別される。
返り値として分割された文字列の配列が返される
splits(パターン)もしくはsplits(パターン; フラグ):
上のsplit(パターン; フラグ)と作用は同じだが、
返り値として文字列の配列ではなく、ストリームが返される
となりますが、結果として配列が返ってくるほうが扱いやすいので、ほぼ利用するのは
split(文字列)
split(パターン; フラグ)
また、基本的に正規表現ではない分、
split(文字列)
split(文字列)
$ jq -s -R '
[ split("\n")[] | select(length > 0) | split(" ") ]
' testdata.ssv
#👇配列として表示
[
[
"山下モゲ雄",
"営業部",
"本社",
"3年"
],
[
"島田フガ子",
"経理部",
"名古屋支部",
"15年"
],
[
"岡田ピポ太",
"製造部",
"山口工場",
"8年"
],
[
"沢口モフ代",
"人事部",
"本社",
"4年"
],
[
"銭形ガメ吉",
"海外部",
"メキシコ支部",
"11年"
]
]
ちなみに、出力をJSONストリームから配列表示にしてくれる
-s/--slurp
-s
\n
-s
またセパレータが
&
$ jq -sR '
[ split("\n")[] | select(length > 0) | split("&") ]
' testdata.asv
#...出力結果は先ほどと同じ
CsvファイルをJqコマンドで扱う場合には、特にSplit関数の取り扱いを理解しておくべきですので、このテクニックの重要度はかなり高いと言えます。
複雑なセパレーターの設定
余談として、Jqでも複雑なルールによる分割が行えるということも確認しておきましょう。
以下は単一の文字列を配列として分割してみる一例です。
ここでは前の節で述べていたように、正規表現で分割する
split(パターン; フラグ)
$ echo 'a, b,c,d, e, +f$g/h i jkl mn+o p' | jq -R '
split("\\s|,|\\+|\\$|/";"g") | map( . | select(length > 0) )
'
#👇出力
[
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"jkl",
"mn",
"o",
"p"
]
この例では
|
$ echo 'a, b,c,d, e, +f$g/h i jkl mn+o p' | jq -R '
split("[\\s,+$/]";"g") | map( . | select(length > 0) )
'
#...出力は先程と同様
また、正規表現に頼らず、通常の
split(文字列)
$ echo 'a, b,c,d, e, +f$g/h i jkl mn+o p' | jq -R '
split(" ") |
map( split(",") | .[] ) |
map( split("+") | .[] ) |
map( split("$") | .[] ) |
map( split("/") | .[] )
'
#👇複数のセパレーターで配列化
[
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"jkl",
"mn",
"o",
"p"
]
このスクリプトのキモは、Jqでのチェーン処理を上手く利用して、split関数による配列化と、後段のmapによる配列の成分分解と再配列化(
map( split(",") | .[] )
まとめ
今回は、CsvファイルをAwkとJqで任意のセパレータを使って配列へと分割する方法を詳しく見て参りました。
どちらのコマンドを使っても、Csv形式で使われる以外のセパレート文字によっても最終的にはCsv形式に変換・統一して編集できることを示すことができました。
今後の記事でもsplit関数は作成するスクリプトなどにひょっこり姿を表すと思いますが、その時はこの記事のことを思い出していただけると幸いです。
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー