Skip to content

Instantly share code, notes, and snippets.

@callmekohei
Created June 1, 2019 11:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save callmekohei/3b2067456cdae97ae273a6aafe4745d6 to your computer and use it in GitHub Desktop.
Save callmekohei/3b2067456cdae97ae273a6aafe4745d6 to your computer and use it in GitHub Desktop.
@ytez さんによる AWK の練習問題

@ytez さんによる AWK の練習問題

1) 各行に連番を振る(ヒント: NR)
2) 大文字→小文字の変換, その逆(ヒント: toupper,tolower)
3) 2文字目〜5文字目だけを切り出す (ヒント: substr)
4) foo を bar に置換する (ヒント: sub)
5) 奇数行だけ取り出す (ヒント: NR%2)

callmekoheiの回答

### 1) 各行に連番を振る(ヒント: NR)
seq 10 15 | awk '{ print NR " => " $0 }'

### 2) 大文字→小文字の変換, その逆(ヒント: toupper,tolower)
echo 'ABC' | awk '{print tolower($0)}'
# abc
echo 'abc' | awk '{print toupper($0)}'
# ABC

### 3) 2文字目〜5文字目だけを切り出す (ヒント: substr)
echo 'abcdefg' | awk '$0 = substr($0, 2,5)'

### 4) foo を bar に置換する (ヒント: sub)
echo "foo bar baz" | awk '{ sub(/foo/ , "bar"); print $0 }'
# bar bar baz 

### 5) 奇数行だけ取り出す (ヒント: NR%2)
seq 10 15 | awk '{if(NR%2 == 0) {print $0}}'
@callmekohei
Copy link
Author

callmekohei commented Jun 1, 2019

上記のやりなおし

### 1) 各行に連番を振る(ヒント: NR)
seq 11 16 | awk '{ print NR " => " $0 }'
# 1 => 11 
# 2 => 12 
# 3 => 13 
# 4 => 14 
# 5 => 15 
# 6 => 16 

### 2) 大文字→小文字の変換, その逆(ヒント: toupper,tolower)
echo 'ABC' | awk '{print tolower($0)}'
# abc
echo 'abc' | awk '{print toupper($0)}'
# ABC

### 3) 2文字目〜5文字目だけを切り出す (ヒント: substr)
echo 'abcdefg' | awk '$0 = substr($0, 2,5-(2-1))'
# bcde

### 4) foo を bar に置換する (ヒント: sub)
echo "foo bar baz" | awk '{ sub(/foo/ , "bar"); print $0 }'
# bar bar baz 

### 5) 奇数行だけ取り出す (ヒント: NR%2)
seq 1 5 | awk '{if ( NR%2 != 0)  print "odd:" $0 }'
# odd:1
# odd:3
# odd:5

### 6)
# 4) のインプットをfoo foo bazにする
# ・sub を2回使って2つとも置換する
# ・上記をgsubを1回使って書き直す
# ・上記をgensubを使って書き直す
echo "bar bar baz" | awk '{ sub(/bar/ , "foo"); sub(/bar/ , "foo"); print $0 }'
echo "bar bar baz" | awk '{ gsub(/bar/ , "foo"); print $0 }'
echo "bar bar baz" | awk '{print gensub(/bar/ , "foo", "g") }'

@callmekohei
Copy link
Author

使用してる AWK

(ins)$ awk --version
GNU Awk 5.0.0, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.1.2)
Copyright (C) 1989, 1991-2019 Free Software Foundation.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.

gawk on osx

see also: macで使いにくいコマンドをLinuxに合わせる

brew install coreutils
brew install gnu-sed
brew install gawk

alias readlink='greadlink'
alias awk='gawk'
alias sed='gsed'
alias date='gdate'

@callmekohei
Copy link
Author

callmekohei commented Jun 1, 2019

追加の課題

@ytezさん。4番目がわかってません。

# 1.
# FPAT変数を使って、””の中にコンマが含まれているような
# CSVでもきちんとカラム分割できる

echo 'foo,"wilkinson,tansan",baz' | awk \
 -v FPAT='([^,]+)|(\"[^\"]+\")' '{print $2}'
# "wilkinson,tansan"
# 2. 配列の基本
# 連想配列であることを理解する。
# splitして要素数を確認して、インデックス値でループ処理できる

BEGIN {

  fruits["apple"]  = 100;
  fruits["banana"] = 200;
  fruits["cherry"] = 300;

  for ( i in fruits ){
    print i , fruits[i]
  }

}
# apple 100
# banana 200
# cherry 300


# 3. ユーザfunction 定義ができる。
BEGIN {
  Hello();
}

function Hello(){
  print "hello world!"
}
# hello world!


# 4. できれば、gensubで正規表現+後方参照+マッチング変数を
# 使って自由自在に置換できるようになってほしい

BEGIN {
  print gensub(/(bar)/,"foo \\1 baz","g", "bar bar baz" )
}
# foo bar baz foo bar baz baz

@callmekohei
Copy link
Author

callmekohei commented Jun 1, 2019

g1にかえる(わかりやすくするために・・・)

# 4. できれば、gensubで正規表現+後方参照+マッチング変数を
# 使って自由自在に置換できるようになってほしい

BEGIN {
  print gensub(/(bar)/,"foo \\1 baz","1", "bar bar baz" )
}
# foo bar baz bar baz

@callmekohei
Copy link
Author

CSVでの同カラムの置換

下記csvにおいて列B,列Dにおいてyyyymmddからyyyy-mm-ddに置き換えができるか問題

お客様コード,年月,好きなもの,最終請求日
A0011223344,20190430,りんご,20180731
A0011223345,20180430,サクランボ,20170224
A0011223348,20290321,ばなな,20110222

下記は実施できたぽよ

2019-04-30 ,2018-07-31, A0011223344,20190430,りんご,20180731
2018-04-30 ,2017-02-24, A0011223345,20180430,サクランボ,20170224
2029-03-21 ,2011-02-22, A0011223348,20290321,ばなな,20110222

foo.bash

#! /bin/bash

cat foo.csv | tail -n +2 | ./foo.awk

foo.awk

#! /bin/awk -f

BEGIN{
  FS=","
}
{
  print ymd($2),","ymd($4)",",$0
}

function ymd(yyyymmdd){
  y = substr(yyyymmdd,0,4);
  m = substr(yyyymmdd,5,2);
  d = substr(yyyymmdd,7,2);
  return y"-"m"-"d

}

@callmekohei
Copy link
Author

なにげにこの掲示板(というのかなwww)、今日のお仕事中にめちゃめちゃ見たぽよ・・・

@callmekohei
Copy link
Author

ytezさんのアドバイスっ

BEGIN{
  FS=","
 OFS=","
}
{
  $2=ymd($2);$4=ymd($4); print $0
}

function ymd(yyyymmdd,    y, m, d) { # ← ここだいじ
  y = substr(yyyymmdd,0,4)
  m = substr(yyyymmdd,5,2)
  d = substr(yyyymmdd,7,2)
  return y "-" m "-" d
}

@callmekohei
Copy link
Author

A0011223344,2019-04-30,りんご,2018-07-31
A0011223345,2018-04-30,サクランボ,2017-02-24
A0011223348,2029-03-21,ばなな,2011-02-22

@callmekohei
Copy link
Author

callmekohei commented Jun 14, 2019

AWKを実務で使ってみた!

任意の 'CSV' から特定の列を抜き出し
3列目 1stソート、1列目で 2ndソート
3列目の重複を削除して
1列目でソートする
1行目を削除する

cat *.csv \
  | cut -d ',' -f 2,7,9,13,19 \
  | LC_ALL='C' sort -t , -k 3,3 -k 1r,1  \
  | awk -F, '{if (!a[$3]++) { print $0 } }' \
  | LC_ALL='C' sort -t , -k 1r,1 \
  | sed -e '1d' \
  > foo.csv

@callmekohei
Copy link
Author

AWKにおける Uniq

上記の復習

Awk's boolean

Status Value
False "" or 0
True Other than those above

Awk's dictionary

dict["apple"] = a
dict["apple"] = b
print dict["apple"] 
# // => b

foo.csv

aaa,123,apple
bbb,456,banana
aaa,789,cherry
ccc,111,lemon
aaa,222,laichi
ddd,333,carrot
aaa,444,grape

foo.bash

cat foo.csv \
  | sort -t, -k 1,1 \
  | awk -f ./foo.awk \

foo.awk

#! /usr/local/bin/awk
BEGIN{
   FS=","
  OFS=","
}
{
  if ( !dict[$1]++ )
    print $0
}

実行結果

aaa,123,apple
bbb,456,banana
ccc,111,lemon
ddd,333,carrot

上記awkコードの意味
ytezさんのツイートを参考に・・・

ポイントは、boolean , dict そして postIncr のところ 

awkのfalseはゼロもしくは空文字のみ。他はすべてtrue

dict(連想配列)は  dict["foo"] と表現する

dict["foo"]++ と後置インクリメンタルすることで
評価された後で下記を実行する
dict["foo"] = dict["foo"] + 1 

@callmekohei
Copy link
Author

callmekohei commented Jun 15, 2019

上記の補足( ytezさんによる補題)

最初にヒットしたaaa以外を出力する

#! /usr/local/bin/awk
BEGIN{
   FS=","
  OFS=","
}
{
  if ( !( dict[$1]++ <= 0 ) )
    print $0
}

結果

aaa,222,laichi
aaa,444,grape
aaa,789,cherry

@callmekohei
Copy link
Author

さらに上記の追記

  if ( !( dict[$1]++ <= 0 ) ) # 2行目以降
  if ( !( dict[$1]++ <= 1 ) ) # 3行目以降
  if ( !( dict[$1]++ <= 2 ) ) # 4行目以降
  if ( !( dict[$1]++ <= 3 ) ) # 5行目以降

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment