awk メモランダム

***
ドットインストールの学習メモです
***

始める前に、VM(VirtualBox)上のフォルダをvscodeでどうやって開こうかと、
苦心惨憺していたが、そういえば、とcyberduckを思い出してやってみたら
早い早い。一瞬で開いた。
今までなんでサイバーダダックがあるのかわかんなかったが
ターミナルだけで開こうとすると、VMにsshトンネル？を繋いでヤンなきゃいけないので
大変そうだった。というかめんどくさそうな割に成果に見合わないと直感的に思った。
作業しててもこれ以上進めてはいけない、と感じたし。
というわけで、テンキュー、ダック。
***

##はじめてのawkプログラム
* myapp.awkを作成

```awk:myapp.awk
{
  print $4
}
```

scores.datの内容

```
2017-06-18 03:51:28 tanaka 181 294 49 278
2017-06-18 08:29:58 taguchi 183 206 10 1
2017-06-18 09:30:18 fkoji 160 198 114 324
2017-06-18 14:47:22 fkoji 273 442 31 274
2017-06-19 02:23:08 fkoji 252 270 416 240
.
.
.
```

ターミナルで実行

```terminal:terminal
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
```

すると

```terminal:terminal
181
183
160
273
252
.
.
.
```
とscores.datの４列目の内容が改行つきで出力される。

短い命令なので、全部ひとまとめに・・・

```terminal:terminal
[vagrant@localhost awk_lessons]$ awk '{print $4}' scores.dat 
```
でもおk。

##フィールドの指定

```bash
{
	print $3,$4   
          #3列目と４列目を表示
	print $3 ":" $4   
          #間に”でコロンを表示
	print $0   
          #全てのフィールドを表示
	print NF, $NF
	      #number of fieldsでレコードに含まれるフィールドの数
	      #$NFで最後のフィールドを意味する
	print NR ":" $0
	      #number of recordsで何番目のレコードかを表す。
	      #上の場合全てのフィールドを行番号つきで表してくれる
}
```

##レコードの指定
レコード番号３以下を表示

```
NR < 3{          ←(パターンと呼ぶ）
	print NR ":" $0　　　←（アクションと呼ぶ）
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
1:2017-06-18 03:51:28 tanaka 181 294 49 278
2:2017-06-18 08:29:58 taguchi 183 206 10 1
```

```bash
<taguchiと$4あ１００より大きい物を抽出>
($3 == "taguchi") && ($4 > 100) {
	print $3,$4
}

<$3がtから始まる物を全て抽出>
$3 ~ /^t.*/ {
	print $0
}
```

##BEGIN,ENDを使ってみよう

```bash
BEGIN { #最初のみ実行
  print "--- START ---"
}
NR > 5 && NR < 10 {
  print $0
}
END { #最後のみ実行
  print "--- END ---"
}
```


```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
--- START ---
2017-06-19 12:44:08 tanaka 195 297 102 102
2017-06-19 13:04:43 fkoji 356 399 277 218
2017-06-20 01:53:41 tanaka 399 67 275 290
2017-06-20 06:14:21 taguchi 213 404 333 199
--- END ---
```
###FS=Field Separater (通常は空白。区切り文字を変える）

```bash
BEGIN {
	FS = "-"
}
NR > 5 && NR < 10 {
  print $1,$2
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
2017 06
2017 06
2017 06
2017 06
```

2017-06-19 12:44:08 tanaka 195 297 102 102
2017-06-19 13:04:43 fkoji 356 399 277 218
2017-06-20 01:53:41 tanaka 399 67 275 290
2017-06-20 06:14:21 taguchi 213 404 333 199
の **- の部分でセパレートして、一番目と二番目を表示した。**

### RS = Record Separater （通常は改行。レコードの終わりを変える）

```bash
NR == 1{
  print $0
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
2017-06-18 03:51:28 tanaka 181 294 49 278
```

だったものが

```bash
BEGIN {
	RS = ":"
}
NR == 1{
  print $0
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
2017-06-18 03
```
となる。
**０３の後の:で終わりと認識された、ということ。**

###OFSとORS
* Output Field Separater
* Output Record Separater

```bash
BEGIN { 
	OFS = "@"
	ORS = "|"
}
NR > 5 && NR < 10 {
  print $3, $4
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
tanaka@195|fkoji@356|tanaka@399|taguchi@213|[vagrant@localhost awk_lessons]$ 
```
**$3と＄４のフィールドの変わり目に ＠ が、
レコードの変わり目に | が入っていることがわかる。**

##変数と演算子

```bash
BEGIN {
	total = 0
}

NR < 4 {
	total += $4
}

END {
	print total
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
524
```

##printfによる出力
書式を指定して出力できる

```bash
NR > 96 {
1. $3名前、$4~7の合計、＄４〜７の平均を表す
	print $3, ($4+$5+$6+$7), (($4+$5+$6+$7)/4)

2. それぞれに項目の名前を表示する
	printf "Name: %s Sum: %d Avg: %f\n", $3,
	($4+$5+$6+$7), (($4+$5+$6+$7)/4)

3. それぞれ１０桁（Avgは小数点２桁も＋）にする。Nameは-を足して左揃えにする。
	printf "Name: %-10s Sum: %10d Avg: %010.2f\n", $3,
	($4+$5+$6+$7), (($4+$5+$6+$7)/4)

4.Sumに３桁ごとに,を入れる。%の後に’を入れる。
	printf "Name: %-10s Sum: %'10d Avg: %010.2f\n", $3,
	($4+$5+$6+$7), (($4+$5+$6+$7)/4)
}
```

```terminal
1.
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
fkoji 1027 256.75
taguchi 1272 318
fkoji 1025 256.25

2.
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: fkoji Sum: 1027 Avg: 256.750000
Name: taguchi Sum: 1272 Avg: 318.000000
Name: fkoji Sum: 1025 Avg: 256.250000

3.
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: fkoji      Sum:       1027 Avg: 0000256.75
Name: taguchi    Sum:       1272 Avg: 0000318.00
Name: fkoji      Sum:       1025 Avg: 0000256.25

4.
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: fkoji      Sum:      1,027 Avg: 0000256.75
Name: taguchi    Sum:      1,272 Avg: 0000318.00
Name: fkoji      Sum:      1,025 Avg: 0000256.25
```

##組み関数を使う
awkで既に用意されてる便利な命令。

```bash
BEGIN {
	print int(3.8) #3が出力
	print length("hello") #5
	print substr("hello", 3) #llo
	print substr("hello", 3, 2) #ll
}
```

```terminal
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
3
5
llo
ll
```
 
##ifによる条件分岐

```bash
NR < 15 {
	print NR ":" $0
	if (NR % 10 == 0) {
		print "------"
	} else if (NR % 5 == 0) {
		print "---"
	} else {
		print "-"
	}
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
1:2017-06-18 03:51:28 tanaka 181 294 49 278
-
2:2017-06-18 08:29:58 taguchi 183 206 10 1
-
3:2017-06-18 09:30:18 fkoji 160 198 114 324
-
4:2017-06-18 14:47:22 fkoji 273 442 31 274
-
5:2017-06-19 02:23:08 fkoji 252 270 416 240
---
6:2017-06-19 12:44:08 tanaka 195 297 102 102
-
7:2017-06-19 13:04:43 fkoji 356 399 277 218
-
8:2017-06-20 01:53:41 tanaka 399 67 275 290
-
9:2017-06-20 06:14:21 taguchi 213 404 333 199
-
10:2017-06-20 08:17:19 fkoji 23 350 204 463
------
11:2017-06-20 17:04:52 fkoji 380 155 232 412
-
12:2017-06-21 00:07:26 tanaka 31 387 54 236
-
13:2017-06-21 12:38:52 tanaka 452 469 5 165
-
14:2017-06-22 01:50:47 tanaka 300 478 168 440
-
15:2017-06-22 13:17:56 tanaka 186 464 381 133
---
```

##whileでの繰り返し

```bash
NR < 4 {
	sum = 0
	i = 4
	while (i <= 7) {
		sum += $i
		i++
	}
	printf "Name: %-10s Sum: %'10d\n", $3, sum
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: tanaka     Sum:        802
Name: taguchi    Sum:        400
Name: fkoji      Sum:        796
```


##for,continue,break

**for** 繰り返し文
**continue**    条件に合ったものをスキップする（無視する）。
**break**      条件に合ったものがあったらそこで終了する。

```bash
NR < 4 {
	sum = 0
	for (i = 4; i <= 7; i++){
		if ($i < 100){
			# continue
			break
		}
		sum += $i
	}
	printf "Name: %-10s Sum: %'10d\n", $3, sum
}
```

```
forのみ
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: tanaka     Sum:        802
Name: taguchi    Sum:        400
Name: fkoji      Sum:        796

continue 100未満は無視
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: tanaka     Sum:        753
Name: taguchi    Sum:        389
Name: fkoji      Sum:        796

break　１００未満が出てきたろそこで終了
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: tanaka     Sum:        475
Name: taguchi    Sum:        389
Name: fkoji      Sum:        796
```

##配列を使う

```bash
BEGIN {
	# rank1 = "Gorld"
	# rank2 = "Silver"
	# rank3 = "Bronz"
	# rank[1] = "Gorld"
	# rank[2] = "Silver"
	# rank[3] = "Bronz"
	split("Gorld Silver Bronz", ranks)
	print ranks[1] #Gorld
	ranks[2] = "Plata"
	print ranks[2] #Plata
	exit
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Gorld
Plata
```

##配列をforで処理

lengthを配列に対して使うと要素数を返してくれる。

```bash
BEGIN {
	split("Gorld Silver Bronz", ranks)
	print "Available Ranks"
	# for (i = 1; i <= 3; i++) {
	for (i = 1; i <= length(ranks); i++) {
		print ranks[i]
	}
	print "------"
}

NR < 4 {
	sum = 0
	for (i = 4; i <= 7; i++){
		if ($i < 100){
			# continue
			# break
		}
		sum += $i
	}
	if (sum > 1000) {
		rank = ranks[1]
	} else if (sum > 800) {
		rank = ranks[2]
	} else {
		rank = ranks[3]
	}
	printf "Name: %-10s Sum: %'10d Rank: %-10s\n", $3, sum, rank
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Available Ranks
Gorld
Silver
Bronz
------
Name: tanaka     Sum:        802 Rank: Silver    
Name: taguchi    Sum:        400 Rank: Bronz     
Name: fkoji      Sum:        796 Rank: Bronz   
```

##関数を自分で作ってみよう

```bash
NR < 4 {
	sum = getSum()
	rank = getRank(sum)
	printf "Name: %-10s Sum: %'10d Rank: %-10s\n", $3, sum, rank
}

function getSum() {
	sum = 0
	for (i = 4; i <= 7; i++){
		sum += $i
	}
	return sum
}

 function getRank(sum) {
	 	split("Gorld Silver Bronz", ranks)
		if (sum > 1000) {
		rank = ranks[1]
		} else if (sum > 800) {
		rank = ranks[2]
		} else {
		rank = ranks[3]
	}
	return rank
 }

```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: tanaka     Sum:        802 Rank: Silver    
Name: taguchi    Sum:        400 Rank: Bronz     
Name: fkoji      Sum:        796 Rank: Bronz    
```

##連想配列を使う

rank[1]などの添字に文字列などを使った配列を**連想配列**と呼ぶ。

```bash
NR < 4 {
	sum = getSum()
	rank = getRank(sum)
	printf "Name: %-10s Sum: %'10d Rank: %-10s\n", $3, sum, rank
}

function getSum() {
	sum = 0
	for (i = 4; i <= 7; i++) {
		sum += $i
	}
	return sum
}

function getRank(sum) {
	 	# split("Gold Silver Bronze", ranks)
		ranks["first"] = "Gold"
		ranks["second"] = "Silver"
		ranks["third"] = "Bronze"
		if (sum > 1000) {
		rank = ranks["first"]
		} else if (sum > 800) {
		rank = ranks["second"]
		} else {
		rank = ranks["third"]
	}
	return rank
 }
```

###連想配列は集計に便利な場面も。
ユーザーごとに全てのスコアの合計を求める。

```bash:変更点
{
	sum = getSum()
	total[$3] += sum
	# total[taguchi...] += sum などの意
}

END {
	for (name in total) {　
#totalから１つずつ添え字を取り出してnameへ
		printf "Name: %-10s Todal: %'10d\n",name, total[name]
	}
}
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: fkoji      Todal:     38,844
Name: taguchi    Todal:     31,756
Name: tanaka     Todal:     28,185
[vagrant@localhost awk_lessons]$ 
```

##グラフを書く

```bash:graph
{
	sum = getSum()
	total[$3] += sum
}
END {
	for (name in total) {
		printf "Name: %-10s Total: %'10d %s\n", name, total[name], getBarGraph(total[name])
	}
}

function getBarGraph(total) {
	s = ""
	for (i = 1; i <= int(total / 1000); i++) {
		s = s "*"
	}
	return s
}

function getSum() {
	sum = 0
	for (i = 4; i <= 7; i++) {
		sum += $i
	}
	return sum
}

function getRank(sum) {
	 	# split("Gold Silver Bronze", ranks)
		ranks["first"] = "Gold"
		ranks["second"] = "Silver"
		ranks["third"] = "Bronze"
		if (sum > 1000) {
		rank = ranks["first"]
		} else if (sum > 800) {
		rank = ranks["second"]
		} else {
		rank = ranks["third"]
	}
	return rank
 }
```

```
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: fkoji      Total:     38,844 **************************************
Name: taguchi    Total:     31,756 *******************************
Name: tanaka     Total:     28,185 ****************************
```