LoginSignup
0
0

More than 3 years have passed since last update.

awk メモランダム

Posted at

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


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


はじめてのawkプログラム

  • myapp.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
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 

すると

terminal
181
183
160
273
252
.
.
.

とscores.datの4列目の内容が改行つきで出力される。

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

terminal
[vagrant@localhost awk_lessons]$ awk '{print $4}' scores.dat 

でもおk。

フィールドの指定

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

レコードの指定

レコード番号3以下を表示

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
<taguchiと$4あ100より大きい物を抽出>
($3 == "taguchi") && ($4 > 100) {
    print $3,$4
}

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

BEGIN,ENDを使ってみよう

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 (通常は空白。区切り文字を変える)

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 (通常は改行。レコードの終わりを変える)

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

だったものが

BEGIN {
    RS = ":"
}
NR == 1{
  print $0
}
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
2017-06-18 03

となる。
03の後の:で終わりと認識された、ということ。

OFSとORS

  • Output Field Separater
  • Output Record Separater
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と$4のフィールドの変わり目に @ が、
レコードの変わり目に | が入っていることがわかる。

変数と演算子

BEGIN {
    total = 0
}

NR < 4 {
    total += $4
}

END {
    print total
}
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
524

printfによる出力

書式を指定して出力できる

NR > 96 {
1. $3名前、$4~7の合計、$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. それぞれ10桁(Avgは小数点2桁も+)にする。Nameは-を足して左揃えにする。
    printf "Name: %-10s Sum: %10d Avg: %010.2f\n", $3,
    ($4+$5+$6+$7), (($4+$5+$6+$7)/4)

4.Sumに3桁ごとに,を入れる。%の後に’を入れる。
    printf "Name: %-10s Sum: %'10d Avg: %010.2f\n", $3,
    ($4+$5+$6+$7), (($4+$5+$6+$7)/4)
}
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で既に用意されてる便利な命令。

BEGIN {
    print int(3.8) #3が出力
    print length("hello") #5
    print substr("hello", 3) #llo
    print substr("hello", 3, 2) #ll
}
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
3
5
llo
ll

ifによる条件分岐

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での繰り返し

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 条件に合ったものがあったらそこで終了する。

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 100未満が出てきたろそこで終了
[vagrant@localhost awk_lessons]$ awk -f myapp.awk scores.dat 
Name: tanaka     Sum:        475
Name: taguchi    Sum:        389
Name: fkoji      Sum:        796

配列を使う

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を配列に対して使うと要素数を返してくれる。

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   

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

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]などの添字に文字列などを使った配列を連想配列と呼ぶ。

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
 }

連想配列は集計に便利な場面も。

ユーザーごとに全てのスコアの合計を求める。

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

END {
    for (name in total) { 
#totalから1つずつ添え字を取り出して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]$ 

グラフを書く

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 ****************************
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0