Table of Contents
awkってなに?
- テキストデータをいろいろ加工したり処理したりする際(=前処理)に活躍するシェルコマンド
- ワンライナーで活躍してくれます
Dependencyと設定
今回は gawk
ベースで紹介します. gawk
が入っていない場合は以下のラインでインストール:
1
% sudo apt install gawk
インストールすると awk
コマンドは gawk
を参照しているはずです. その確認方法は、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
% sudo update-alternatives --display awk
awk - auto mode
link best version is /usr/bin/gawk
link currently points to /usr/bin/gawk
link awk is /usr/bin/awk
slave awk.1.gz is /usr/share/man/man1/awk.1.gz
slave nawk is /usr/bin/nawk
slave nawk.1.gz is /usr/share/man/man1/nawk.1.gz
/usr/bin/gawk - priority 10
slave awk.1.gz: /usr/share/man/man1/gawk.1.gz
slave nawk: /usr/bin/gawk
slave nawk.1.gz: /usr/share/man/man1/gawk.1.gz
/usr/bin/mawk - priority 5
slave awk.1.gz: /usr/share/man/man1/mawk.1.gz
slave nawk: /usr/bin/mawk
slave nawk.1.gz: /usr/share/man/man1/mawk.1.gz
awkコマンド
- 簡単な行ベースのテキストファイルを処理するコマンド
- 演算機能もあり、プログラミング言語としても使用されています
- イメージとしては
grep
にプログラム機能をつけたコマンド
Syntax
1
% awk [option] [command] [file path]
Option一覧
短いオプション | 長いオプション | 意味 |
---|---|---|
-f program-file |
--file program-file |
awk への第 1 引数を用いるかわりに、 AWK プログラムをファイル program-file から読み込む |
-F fs |
--field-separator fs |
入力フィールドセパレータ (変数 FS の値)をfsする field seperatorのdefaultはwhite space |
-v var=val |
--assign var=val |
プログラムを実行する前に、変数 var に値 val を設定 |
awkで使用できる主な組み込み変数
変数名 | 意味 |
---|---|
ARGC | 引数の個数 |
ARGV | 引数(配列) |
ENVIRON | 環境変数を収めた連想配列。例えば環境変数LANGならばENVIRON[“LANG”]と参照できる |
FILENAME | 現在処理しているファイルの名前 |
FNR | 現在処理しているファイルのレコード番号 |
FS | フィールドの区切り文字(-Fオプションで変更可能、デフォルトはスペース) |
NR | 現在処理しているレコード番号(行番号) |
OFS | 出力時のフィールドの区切り(デフォルトは空白) |
ORS | 出力時のレコードの区切り(デフォルトは改行) |
RS | レコードの区切り(デフォルトは改行) |
awkコマンドを体験する
(1) 1~5までのsequenceのうち偶数のみを抽出する
1
2
3
% seq 5 | awk '/[24]/'
2
4
awk /正規表現/
でgrep 正規表現
と同じ挙動になります. 別の書き方として2で割り切れる数値が偶数であること利用して次のような書き方も可能です.
1
2
3
% seq 5 | awk '$1%2==0'
2
4
awkコマンドにシングルクォートで囲まれた箇所の中の$1
は読み込んだ行の一列目を意味します.
awkコマンドはzshやbashといったシェルで定義された関数以外にawkの中で定義された関数を呼び出して前処理を実行します.
そのため、「awk独自の関数に基づいた命令です」ということを明示する必要がありますが、 これが理由でawkコマンドにシングルクォートで囲った範囲が出てくると考えてください.
(2) 1~5までのsequenceのうち偶数のみを抽出する & 抽出結果に「偶数」という文字列を付与する
解答
1
2
3
% seq 5 | awk '$1%2==0{printf("%s, 偶数\n", $1)}'
2, 偶数
4, 偶数
別解
1
% seq 5 | awk '$1%2==0{print($1, "偶数")}'
今回は、抽出結果に「偶数」という文字列を付与して出力したいので、何かしらの形で「偶数」という文字列をprintf
に与える必要があります.
出力したい文字列はダブルクォートで囲むことで実現できます. シングルクォートだとawkコマンド適応領域という意味とコンフリクトしてしまうので、「ダブルクォート」と理解すると覚えやすいです.
はじめに、偶奇判定のgrep(前提条件コマンド)を実行してから、{}
で囲まれたawkコマンドを実行しています.
(3) 1~5までのsequenceのうち、数値に応じて奇数偶数という文字列を付与する
解答
1
2
3
4
5
6
% seq 5 | awk '$1%2==0{print($1, "偶数")}$1%2{print($1, "奇数")}'
1 奇数
2 偶数
3 奇数
4 偶数
5 奇数
別解
1
2
3
4
5
6
% seq 5 | awk '{a = ""; $1%2 ? a = "奇数" :a = "偶数"; print($1, a)}'
1 奇数
2 偶数
3 奇数
4 偶数
5 奇数
「別解」では、CONDITION ? 真の場合に返す値 : 偽の場合に返す値
という三項演算子を用いて記述しています.
awkコマンドのフロートしては、
- 変数
a
を空文字で定義 - 三項演算子を用いて
a
をアップデート - 読み込んだテキストの一列目とその値に応じた
a
をターミナルに出力
という3つのコマンドを実行しています. この3つのコマンドの接続方法は、セミコロン;
でコマンドをつなぐことで可能となります.
(4) 1~5までのsequenceのうち、数値に応じて奇数偶数という文字列を付与する
1
% seq 5 | awk 'BEGIN{a=0}$1%2==0{print($1, "偶数")}$1%2{print($1, "奇数")}{a+=$1}END{print("合計", a)}'
BEGIN
,END
は、それぞれ「awkが1行目の処理を始める前」「awkが最終行の処理を終えた後」という状況にマッチします.- 上記は、最初に変数 a を0に初期化して、行の処理のたびに+1のincrementを実施し、最後にその時のaの状態を出力するという処理です.
(5) awkコマンドを用いてディスクの空き領域を表示する
解答
1
2
3
% df -m | awk 'NR==1 {print "1G-Blocks", "GB-"$4}; NR>=2 {x+=$2/(2**10); y+=$4/(2**10)}; END {print x, y}'
1G-Blocks GB-Available
541.072 458.293
- 「df」を実行すると、ディスクの空き領域が表示されます
-m
optionは指定したサイズの倍数で表示するオプション(MB単位)
別解
1
% df -h --total
(6) CSVファイルの各フィールドを文字列結合して複数ディレクトリを作成する
1
2
3
4
5
6
7
%ls
test.csv
% cat test.csv
store_name,store_code
a,100
b,101
c,101
上記のようにtest.csv
の2つのfieldにstore_name,store_code
がカレントワーキングディレクトリに格納されているとします.
この2つのfieldを組合せて、<store_name>_<store_code>
というdirectoryをカレントワーキングディレクトリ以下に作成したいとします.
この場合のshell scriptは以下.
1
2
3
% awk -F "," 'NR>1{print $1"_"$2}' test.csv| awk '{system("mkdir "$1)}'
% ls
a_100/ b_101/ c_101/ test.csv
解説
awk -F "," 'NR>1{print $1"_"$2}' test.csv
とawk '{system("mkdir "$1)}'
の2つのパートに分けて考えます.
前者はCSVファイルをカンマセパレートで各カラムをREADし、それらを文字列結合し出力しています. その後、その出力結果を後者に渡してsystem()
でLinuxコマンドをawk
の中で呼び、
mkdir
でディレクトリを作成しています.
awkコマンドを用いたデータ整形
このセクションで用いるデータ
- ソース:スタンドパラメータ - 甚平 - ジョジョの奇妙な冒険
- ファイル名:
example.csv
- クリップボードにコピーした後、
xclip -out -selection clipboard > example.csv
で簡単に複製できます
ID,stand_name,user_name,power,speed,range,duration,agility,growth
001,スタープラチナ,空条承太郎,A,A,C,A,A,A
002,マジシャンズレッド,モハメド・アヴドゥル,B,B,C,B,C,D
003,ハーミットパープル,ジョセフ・ジョースター,D,C,D,A,D,E
004,ハイエロファントグリーン,花京院典明,C,B,A,B,C,D
005,タワー・オブ・グレー,グレーフライ,E,A,A,C,E,E
006,シルバーチャリオッツ,J・P・ポルナレフ,C,A,C,B,B,C
007,ダークブルームーン,キャプテン・テニール,C,C,C,B,C,D
008,力・ストレングス,フォーエバー,B,D,D,A,E,E
009,エボニーデビル,呪いのデーボ,D,D,A,B,D,B
010,イエローテンパランス,ラバーソール,D,C,E,A,E,D
011,ハングドマン,J・ガイル,C,A,A,B,D,D
012,エンペラー,ホル・ホース,B,B,B,C,E,E
013,エンプレス,ネーナ,C,E,A,A,D,D
014,ホウィール・オブ・フォーチュン,ズィー・ズィー,B,D,D,A,E,D
015,ジャスティス,エンヤ婆,D,E,A,A,E,E
016,ラバーズ,鋼入りのダン,E,D,A,A,D,E
017,サン,アラビア・ファッツ,B,E,A,A,E,E
018,デス・サーティーン,マニッシュ・ボーイ,C,C,E,B,D,B
019,ジャッジメント,カメオ,B,B,C,B,D,D
020,ハイプリエステス,ミドラー,C,B,A,A,D,D
021,ザ・フール,イギー,B,C,D,C,D,C
022,ゲブ神,ンドゥール,C,B,A,B,D,D
023,トト神,ボインゴ,E,E,E,A,E,E
024,クヌム神,オインゴ,E,E,E,A,E,E
025,アヌビス神,キャラバン・サライ,B,B,E,A,E,C
026,バステト女神,マライア,E,E,B,A,E,E
027,セト神,アレッシー,D,D,E,C,D,D
028,オシリス神,ダニエル・J・ダービー,E,D,D,C,D,D
029,ホルス神,ペット・ショップ,B,B,D,C,E,C
030,アトゥム神,テレンス・T・ダービー,D,C,D,B,D,D
031,ティナー・サックス,ケニー・G,E,E,D,A,E,E
032,クリーム,ヴァニラ・アイス,B,B,D,C,C,D
033,ザ・ワールド,DIO,A,A,C,A,B,B
awkの組み込み変数
変数 | 説明 |
---|---|
NR | the current number of the input records |
NF | the number of fields of the current record |
FS | The input field separator defaultはwhite space FSで区切り文字を設定できる |
RS | The input record separator defaultは改行 |
OFS | The output field separator print出力時のfieldの区切り文字を指定 |
ORS | The output record separator print出力時のrecordの区切り文字 |
FPAT | The input field pattern どのようなパターンをフィールドとして認識するかを定義することができる |
例題 (1) 各レコードのfieldの数を出力する
1
2
3
4
5
6
% awk -F "," '{print NF, $0}' example.csv | head -n 5
9 ID,stand_name,user_name,power,speed,range,duration,agility,growth
9 001,スタープラチナ,空条承太郎,A,A,C,A,A,A
9 002,マジシャンズレッド,モハメド・アヴドゥル,B,B,C,B,C,D
9 003,ハーミットパープル,ジョセフ・ジョースター,D,C,D,A,D,E
9 004,ハイエロファントグリーン,花京院典明,C,B,A,B,C,D
例題 (2) ファイルの行数を出力する
1
2
% awk 'END {print NR}' example.csv
34
or, シンプルにwc
コマンドを用いて改行数をカウントして
1
2
% cat example.csv | wc -l
33
指定した行を出力する
先頭5行を表示したい場合
任意の先頭の行数を出力したい場合は、一般的にはhead
コマンドを用います.
1
2
3
4
5
6
% head -n 5 example.csv
ID,stand_name,user_name,power,speed,range,duration,agility,growth
001,スタープラチナ,空条承太郎,A,A,C,A,A,A
002,マジシャンズレッド,モハメド・アヴドゥル,B,B,C,B,C,D
003,ハーミットパープル,ジョセフ・ジョースター,D,C,D,A,D,E
004,ハイエロファントグリーン,花京院典明,C,B,A,B,C,D
awk
コマンドを用いて表示する場合は
1
% awk 'NR<=5 {print}' example.csv
6行目から10行目を表示したい場合
1
2
3
4
5
6
% awk 'NR<=10 && NR>=6 {print}' example.csv
005,タワー・オブ・グレー,グレーフライ,E,A,A,C,E,E
006,シルバーチャリオッツ,J・P・ポルナレフ,C,A,C,B,B,C
007,ダークブルームーン,キャプテン・テニール,C,C,C,B,C,D
008,力・ストレングス,フォーエバー,B,D,D,A,E,E
009,エボニーデビル,呪いのデーボ,D,D,A,B,D,B
headerを含めたい場合は
1
2
3
4
5
6
7
% awk '(NR<=10 && NR>=6)||(NR==1) {print}' example.csv
ID,stand_name,user_name,power,speed,range,duration,agility,growth
005,タワー・オブ・グレー,グレーフライ,E,A,A,C,E,E
006,シルバーチャリオッツ,J・P・ポルナレフ,C,A,C,B,B,C
007,ダークブルームーン,キャプテン・テニール,C,C,C,B,C,D
008,力・ストレングス,フォーエバー,B,D,D,A,E,E
009,エボニーデビル,呪いのデーボ,D,D,A,B,D,B
RowのFilter
承太郎を含む行を抽出する場合
1
2
% awk '/承太郎/ {print}' example.csv
001,スタープラチナ,空条承太郎,A,A,C,A,A,A
headerを含めたい場合は、
1
2
3
% awk '/承太郎/||(NR==1) {print}' example.csv
ID,stand_name,user_name,power,speed,range,duration,agility,growth
001,スタープラチナ,空条承太郎,A,A,C,A,A,A
承太郎 or Dioを含む行を抽出する場合
1
2
3
% awk '/承太郎|DIO/ {print}' example.csv
001,スタープラチナ,空条承太郎,A,A,C,A,A,A
033,ザ・ワールド,DIO,A,A,C,A,B,B
Jという文字列を含む行のうちポルという文字列があとに続く行を抽出
Jという文字列を含む行は3つあります
1
2
3
4
% awk '/J/ {print}' example.csv
006,シルバーチャリオッツ,J・P・ポルナレフ,C,A,C,B,B,C
011,ハングドマン,J・ガイル,C,A,A,B,D,D
028,オシリス神,ダニエル・J・ダービー,E,D,D,C,D,D
このうち、「ポル」という文字列が後に出現する行を抽出したい場合は
1
2
% awk '/J.*ポル/ {print}' example.csv
006,シルバーチャリオッツ,J・P・ポルナレフ,C,A,C,B,B,C
または、
1
2
% awk '/J.{1,}ポル/ {print}' example.csv
006,シルバーチャリオッツ,J・P・ポルナレフ,C,A,C,B,B,C
「神」という文字を含む行を除外して出力したい場合
スターダストクルセイダースは「オシリス神」など「神」を名前に含むスタンドが数体出現します. それらを除くスタンダ一覧を表示したいとします
1
% awk '!/神/ {print}' example.csv
Columnのフィルター
特定のカラムのみコンソールに出力したい場合
- シンボル
$
と数字の組合せを用いることで特定のカラムを指定することができます - field seperatorのdefaultはwhite spaceなので、csv fileを対象に操作するときは
FS = ","
の指定をします - スタンドと使用者の組をここでは抽出したいとします
解答
1
% awk -F ',' '{print $2, $3}' example.csv
別解
1
% awk '{print $2, $3}' FS=',' example.csv
or
1
% awk 'BEGIN{FS=","} {print $2, $3}' example.csv
カラム別に条件をつけて行を抽出する
- power, speed fieldがAのスタンドのみを抽出したいとします
- header行も出力したいとします
1
2
3
4
% awk -F ',' 'NR==1 || ($4=="A" && $5 == "A") {print $2, $3}' example.csv
stand_name user_name
スタープラチナ 空条承太郎
ザ・ワールド DIO
IDが5以下の行のみを抽出したい場合は
1
2
3
4
5
% awk -F"," '$1<5{print $0}' example.csv
001,スタープラチナ,空条承太郎,A,A,C,A,A,A
002,マジシャンズレッド,モハメド・アヴドゥル,B,B,C,B,C,D
003,ハーミットパープル,ジョセフ・ジョースター,D,C,D,A,D,E
004,ハイエロファントグリーン,花京院典明,C,B,A,B,C,D
スタンド使用者の名前がアルファベットを含む行のみ抽出する
1
2
3
4
5
6
7
8
% awk -F"," 'NR==1 || $3~/[A-Za-z]/{print $0}' example.csv
ID,stand_name,user_name,power,speed,range,duration,agility,growth
006,シルバーチャリオッツ,J・P・ポルナレフ,C,A,C,B,B,C
011,ハングドマン,J・ガイル,C,A,A,B,D,D
028,オシリス神,ダニエル・J・ダービー,E,D,D,C,D,D
030,アトゥム神,テレンス・T・ダービー,D,C,D,B,D,D
031,ティナー・サックス,ケニー・G,E,E,D,A,E,E
033,ザ・ワールド,DIO,A,A,C,A,B,B
もし「スタンド使用者の名前がアルファベットを含まない行のみ抽出」したい場合は
1
% awk -F"," 'NR==1 || $3!~/[A-Za-z]/{print $0}' example.csv
Delimiterを変更して出力したい場合
- csvファイルをtsvとして出力します
解答
1
2
3
4
5
6
7
% awk 'BEGIN{FS=",";OFS="\t"} {for(i=1;i<=NF-1;i++) printf $i" "; print ""}' example.csv > example.tsv
% head -n 5 example.tsv
ID stand_name user_name power speed range duration agility
001 スタープラチナ 空条承太郎 A A C A A
002 マジシャンズレッド モハメド・アヴドゥル B B C B C
003 ハーミットパープル ジョセフ・ジョースター D C D A D
004 ハイエロファントグリーン 花京院典明 C B A B C
別解: gsub
1
% awk 'gsub(",","\t")' example.csv
- gsub functionはglobal substitutionの略
別解: sed version
1
% sed 's/,/\t/g' example.csv > example.tsv
- この時、
-i
optionはつけないこと
複数のcsv fileを結合したい場合
example_2.csv
という以下のファイルを準備します. このファイルとexample.csv
のファイルを結合し、新たなmerged_with_header.csv
を作りたいとします.
Data
ID,stand_name,user_name,power,speed,range,duration,agility,growth
034,クレイジー・ダイヤモンド,東方仗助,A,A,D,B,B,C
035,アクア・ネックレス,片桐安十朗,C,C,A,A,C,E
036,ザ・ハンド,虹村億泰,B,B,D,C,C,C
037,バッド・カンパニー,虹村形兆,B,B,C,B,C,C
038,レッド・ホット・チリ・ペッパー,音石明,A,A,A,A,C,A
039,錠前,小林玉美,E,E,A,A,E,E
040,エコーズACT1,広瀬康一,E,E,B,B,C,A
041,サーフィス,間田敏和,B,B,C,B,C,C
042,ラブ・デラックス,山岸由花子,B,B,C,A,E,B
043,エコーズACT2,広瀬康一,C,D,B,B,C,A
044,パール・ジャム,トニオ・トラサルディー,E,C,B,A,E,C
045,アクトン・ベイビー,静・ジョースター,E,E,なし,A,E,A
046,ヘブンズ・ドアー,岸辺露伴,D,B,B,B,C,A
047,ラット,虫喰い&虫喰いでない,B,C,D,B,E,C
048,ハーヴェスト,矢安宮重清,E,B,A,A,E,C
049,キラークイーン,吉良吉影,A,B,D,B,B,A
050,シンデレラ,辻彩,D,C,C,C,A,C
051,シアーハートアタック,吉良吉影,A,C,A,A,E,A
052,エコーズACT3,広瀬康一,B,B,C,B,C,A
053,アトム・ハート・ファーザー,吉良吉廣,E,E,なし,A,E,E
054,ボーイ・Ⅱ・マン,大柳賢,C,B,C,A,C,C
055,アース・ウインド・アンド・ファイヤー,支倉未起隆,C,C,なし,A,C,C
056,ハイウェイ・スター,墳上裕也,C,B,A,A,E,C
057,ストレイ・キャット,猫草,B,E,なし,A,E,C
058,スーパー・フライ,鋼田一豊大,E,E,なし,A,E,E
059,エニグマ,宮本輝之輔,E,E,C,A,C,C
060,チープ・トリック,乙雅三,E,E,E,A,E,E
061,キラークイーン・バイツァ・ダスト,吉良吉影,B,B,A,A,D,A
解答
1
% awk '(NR == 1) || (FNR > 1)' *.csv > merged_with_header.csv
- 必ずしもIDはsortされていないことに注意
NR == 1
は最初に読み込んだファイルの先頭行という意味- FNRは処理しているファイルの行を意味します
もし,headerを除去してmergedしたい場合は
1
% awk '(FNR > 1)' *.csv > merged_without_header.csv
IDでsortした形で結合したい場合
csvsort
- この手法はIDの表現が変わってしまうので非推奨
1
2
3
4
5
6
7
% csvsort merged_with_header.csv > sorted_merged.csv
% head -n 5 sorted_merged.csv
ID,stand_name,user_name,power,speed,range,duration,agility,growth
1,スタープラチナ,空条承太郎,A,A,C,A,A,A
2,マジシャンズレッド,モハメド・アヴドゥル,B,B,C,B,C,D
3,ハーミットパープル,ジョセフ・ジョースター,D,C,D,A,D,E
4,ハイエロファントグリーン,花京院典明,C,B,A,B,C,D
headとtailの組合せでsort (推奨)
1
% head -n1 merged_with_header.csv && tail -n+2 merged_with_header.csv | sort > sorted_merged_2.csv
なお、csvsort
で作成したファイルとの差分の行数を確認すると
1
2
% diff -u sorted_merged.csv sorted_merged_2.csv | awk 'NR>3 && /+/{print NR}' | wc -l
61
特定のフィールドの部分文字列を抽出したい場合:substr()
特定のフィールドの部分文字列を抽出したいとします.
1
% echo "2021/09/01 09:00:00 UTC,10,200,3000"
上記の文字列から日付部分のみ, 2021/09/01
を抽出したい場合、substr()
関数を用いることで
文字列の頭n文字目からm文字を抜き出して表示することができます.
1
2
% echo "2021/09/01 09:00:00 UTC,10,200,3000"|awk -F, 'BEGIN{OFS=","}{print substr($1, 1, 10),$2,$3,$4}'
2021/09/01,10,200,3000
末尾から文字を抜き出したい場合
123456789, abcdefghijklmn
というレコードが与えられた時、カンマセパレートでそれぞれのフィールドから後ろから数えて5文字を抽出したいとします.
substr(field, start, length)
のうちlengthを省略すると、startから文末までを抽出してくれる機能を利用します.
1
2
% echo "123456789, abcdefghijklmn"|awk -F, '{print substr($1, length($1)-4), substr($2, length($2)-4)}'
56789 jklmn
FPATを用いたフィールド内にカンマへの対応
CSVファイルには「フィールド内にカンマを含む場合にはフィールドをダブルクォートで括る」というルールがあります. 文字列内に,
が含まれるケースや、売上金額などの数値データで初期設定の関係で3桁カンマ区切りのデータが含まれるCSVファイルを扱うとき、単純に awk -F","
と指定してしまうと、自分では想定していない形でフィールドが定義される可能性があります.
例:文字列内にカンマを含むケース
1
2
% echo '"¥10,000",¥100,"¥99,000"' |awk -F"," '{print $2}'
000"
¥100
が期待される出力結果ですが、"¥10,000"
の000"
が出力されてしまいます.
組込変数FPATの導入
いままではフィールドセパレーターを定義してawk
コマンドを用いていましたが、
フィールド内にカンマが含まれる場合はフィールドのパターンを定義することで対応することができます. 例として、
1
2
% echo '"¥10,000",¥100,"¥99,000"' |awk -v FPAT='([^,]+)|(\"[^\"]+\")' '{print $2}'
¥100
上記の例では([^,]+)|(\"[^\"]+\")
という正規表現をFPATに指定することで「カンマを含まない、またはダブルクォートで囲まれていて、その中にダブルクォートを含まない文字列」というフィールドパターンを定義しています.
正規表現 | 意味 |
---|---|
[^...] |
角括弧に含まれる文字以外にマッチ |
+ |
直前の文字が 1回以上 繰り返す場合にマッチ 最長一致, 条件に合う最長の部分に一致 |
| |
OR条件 |
REMARKS
FPAT
を渡す場合は、-F
のセパレーター指定してはいけない- 二重クォートでエスケープされたレコードは
FPAT='([^,]+)|(\"[^\"]+\")'
で対応することができません
Markdownファイルの編集に役に立つawkコマンドの紹介
順序付きリストの整形
問題
Markdownでは、行頭が数字とドットとspaceではじまる行は順序付きリストと呼ばれ、何かのリストを各項目別に番号を振って列挙する際によく使用されます.
いま以下のように, item.md
が与えられたとします:
1
2
3
4
5
6
7
8
9
10
11
# AAA
1. fasukdhals
1. jhdlasjhdkla
1. 1jakl;dj;asld;l1.17638
# BBB
7. 86789132789
9. slikrujdhklsjfkljsljf
10. ksfhdljksdhfhsdlj
これは順番通りになっていないので、行の位置は変えずに、行頭の数字を振り直し、item_fixed.md
を作成したいとします. つまり、item_fixed.md
の中身は
1
2
3
4
5
6
7
8
9
10
11
# AAA
1. fasukdhals
2. jhdlasjhdkla
3. 1jakl;dj;asld;l1.17638
# BBB
1. 86789132789
2. slikrujdhklsjfkljsljf
3. ksfhdljksdhfhsdlj
解答
1
% cat item.md|awk '/^[0-9]+\.\s/{a++;$1=a".";print}/^#/{a=0}!/^[0-9]+\./' > item_fixed.md
解説
awk '/正規表現/'
はgrep 正規表現
と同じ挙動'/^[0-9]+\.\s/
,/^#/
,!/^[0-9]+\./'
の3つのルールを指定'/^[0-9]+\./
: 行頭が数字, ドット, spaceで始まる行を抽出/^#/
: 行頭がシャープから始まる行を抽出!/^[0-9]+\./'
: 行頭が数字, ドット, spaceで始まらない行を抽出
/^#/{a=0}
: 見出しマークを目印にa
の値をリセット
References
- 1日1問、半年以内に習得 シェル・ワンライナー160本ノック
- とほほのAWK入門
- 「シェル芸」に効く AWK処方箋 第3回
- CSVと親しくなるAWK術
- スタンドパラメータ - 甚平 - ジョジョの奇妙な冒険
- StackExchange>Create directory and files from csv file
(注意:GitHub Accountが必要となります)