LoginSignup
0
0

More than 3 years have passed since last update.

awk スクリプト メモ

Last updated at Posted at 2021-03-10

awkスクリプトの備忘録メモです。

環境

  • mac OS
  • Bash
  • awk
  • iTerm2

awkの動き

ターミナルからコマンドを入力して試してみます。

echo

$ echo -e "001 aaa\n002 bbb\n003 ccc"
-eオプションで文字列中にエスケープシーケンスが使えるようになる。

1行ごとに処理する

awkの基本の動作。
{ action } action部で標準入力から渡されたテキストを1行づつ処理します。

$ echo -e "001 aaa\n002 bbb\n003 ccc" | awk '{ print }'
001 aaa
002 bbb
003 ccc

条件に合った入力だけ受け入れる

awk 'pattern { action }'を使って、actionに渡す前にpattern部に条件を指定して条件に合ったテキストだけをaction部に渡すことができます。

$ echo -e "001 aaa\n002 bbb\n003 ccc" | awk '$1 ~ /"001"/ { print }'
Kazu-MacBook-Pro:awk-sample kazoo$ echo -e "001 aaa\n002 bbb\n003 ccc" | awk '$1 ~ /001/ { print }'
001 aaa
Kazu-MacBook-Pro:awk-sample kazoo$ echo -e "001 aaa\n002 bbb\n003 ccc" | awk '$1 ~ /(001)/ { print }'
001 aaa
Kazu-MacBook-Pro:awk-sample kazoo$ echo -e "001 aaa\n002 bbb\n003 ccc" | awk '$1 ~ /(001|002)/ { print }'
001 aaa
002 bbb

例えば、空の行がある場合に、パターン部に正規表現を利用して空行以外を処理できます。

$ echo -e "001 aaa\n002 bbb\n\n003 ccc" | awk '{ print }'
001 aaa
002 bbb

003 ccc
Kazu-MacBook-Pro:awk-sample kazoo$ echo -e "001 aaa\n002 bbb\n\n003 ccc" | awk '/^$/ { print }'

Kazu-MacBook-Pro:awk-sample kazoo$ echo -e "001 aaa\n002 bbb\n\n003 ccc" | awk '/[^$]/ { print }'
001 aaa
002 bbb
003 ccc

BEGIN

BEGINパターンを利用して前処理ができる。

例えば、RS(入力レコードセパレーター)を標準の改行から空白に設定する。

$ echo -e "001,aaa 002,bbb  003,ccc" | awk 'BEGIN{ RS = " " }{ print }'
001,aaa
002,bbb

003,ccc

組み込み変数

awk言語にもシステムで定義された特別な変数があります。

FS

フィールドセパレーター。入力行のフィールド区切り文字が設定されています。
デフォルトは空白。

RS

レコードセパレーター。入力行のレコード区切り文字が設定されています。
デフォルトは改行文字。

NR

1から始まるカレントレコードの番号が設定されています。

NF

カレントレコードのフィールド数が設定されています。

$ echo -e "001,aaa 002,bbb  003,ccc" | awk 'BEGIN{ RS = " " }{ print NR, NF, $0 }'
1 1 001,aaa
2 1 002,bbb
3 0
4 1 003,ccc
$ echo -e "001,aaa 002,bbb  003,ccc" | awk 'BEGIN{ RS = " " }{ if(NF != 0){ print NR, NF, $0 } }'
1 1 001,aaa
2 1 002,bbb
4 1 003,ccc

bashの中でawkを利用する

bashの中でawkを利用する場合の書き方です。

読み込みファイルはShift-JISで用意されている前提

t1-sjis.text
Id,IsDelete,ParentId,Name
"00001",0,"000001","サンプルファイル1.txt"
"00002",0,"000001","サンプルファイル2.txt"
"00003",0,"000002","サンプルファイル3.txt"

スクリプトは下記の感じ

awk-sample01.sh
#! /bin/sh

cat t1-sjis.csv | \
nkf -Sw | \
awk -F, \
'{
    if (NR != 1) {
        system("if test ! -d "$3"; then mkdir "$3"; fi")
        system("if test -d "$3"; then cp "$1" ./"$3"/"$4"; fi")
        print NR
        print $1,$3,$4
    }
}'

説明

  • nkf -Sw nkfでShift-jisをutf-8に変換
  • NR != 1 1行目のヘッダーは処理しない
  • system("if test ! -d "$3"; then mkdir "$3"; fi") ディレクトリが無かったら作る

変数と配列

  • awkの配列とハッシュには違いが無いので、文字列をキーにセットすれば配列はハッシュになる。
  • ハッシュの取り出しは for (value in array) array[value] とする。
awk-sample02.sh
#! /bin/sh

cat t1-sjis.csv | \
nkf -Sw | \
awk -F, \
'{
    array[$3] = $4
    print array[$3]
    print $1,$3,$4
    value01="Hello"
}
END {
    for ( item in array)
        print item, array[item]
    print value01
}
'
$ ./awk-sample02.sh
Name
Id ParentId Name
"サンプルファイル1.txt"
"00001" "000001" "サンプルファイル1.txt"
"サンプルファイル2.txt"
"00002" "000001" "サンプルファイル2.txt"
"サンプルファイル3.txt"
"00003" "000002" "サンプルファイル3.txt"
ParentId Name
"000002" "サンプルファイル3.txt"
"000001" "サンプルファイル2.txt"
Hello

シェルの変数をawkに渡す

$2はシェルのコマンドパラメータ
awk の -vオプションを使ってawkで利用する変数にシェルの変数をセットする

awk -v attachments_dir="$2" -F, \
'
BEGIN {
    print attachments_dir
}
'

2つ以上の変数を渡すとき

awk -v file_name="$1" -v attachments_dir="$2" -F, \
'
BEGIN {
    print file_name attachments_dir
}
'

ソート

パイプでソートする

パイプでsortコマンドに渡すことでソート処理を行う

    for ( item in array)
        print item, array[item] | "sort"
"000001" "サンプルファイル2.txt"
"000002" "サンプルファイル3.txt"
ParentId Name

ループ

whileループ

    while ("ls ./"attachments_dir";" | getline) {
        ls_out[$0] = ++i
    }

forループ

※ls_outは連想配列

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

ファイル

ファイルオープン・クローズ

print文をリダイレクトするとファイルが作成される。
">>"を使うと既存ファイルに追記できる。

awk-sample001.sh
#!/bin/bash

awk \
'
BEGIN {
    # file open
    print > "sample1.csv"
    close("sample1.csv")
}
'

ファイル出力

awk-sample002.sh
#!/bin/bash

awk \
'
BEGIN {
    # file open
    print "Hello awk" > "sample1.txt"
    # file close
    close("sample1.txt")
}
'

ファイル入力

awk-sample003.sh
#!/bin/bash

awk \
'
{
    print $0
}
'
$ cat sample.csv | ./awk-sample003.sh

コマンドラインパラメータの利用

※sample.csv は読み込み対象のファイル
test は if [ ! -f "$1" ]; then でも同じ

awk-sample003.sh
#!/bin/bash

if test ! -f "$1"; then
    echo "$1"'ファイルが存在しません'
    exit 1
fi
cat "$1" | \
awk \
'
{
    print $0
}
'
exit 0
$ ./awk-sample003.sh
ファイルが存在しません
$ ./awk-sample003.sh sample.csv

文字列処理

文字列を分割する

split()

split("文字列", "配列", "分割文字")
戻り値:配列の要素数
配列の添字は1から始まるので注意。

awk-sample005.sh
#!/bin/bash

# if test ! -f "$1"; then
if [ ! -f "$1" ]; then
    echo "$1"'ファイルが存在しません'
    exit 1
fi
cat "$1" | \
nkf -Sw | \
awk -F, \
'
{
    split($2, name, " ")
    print $1 "," $2 "," name[1] "\"" "," "\""name[2]
}
'
exit 0

※sample01.csvの中身は、cybozu kintoneのアプリストア「従業員名簿アプリ」のサンプルデータをお借りしております。

$ cat sample01.csv | nkf -Sw | head -5
"従業員番号(必須)","氏名","雇用形態","入社日","生年月日","TEL","メールアドレス"
"001","富田 美波","正社員","2014/04/01","1992/01/14","0801234****","minami-toda@sample.sample"
"002","山本 健太","正社員","2015/05/29","1985/05/25","0908989****","yamamoto123@sample.sample"
"003","佐藤 昇","正社員","2017/05/29","1986/05/16","0001234****","sato@sample.sample"
"004","太田 隆","正社員","2018/05/29","1977/11/01","0705678****","ohta-takashi@sample.sample"

$ ./awk-sample005.sh sample01.csv | head -5
"従業員番号(必須)","氏名","氏名"","
"001","富田 美波","富田","美波"
"002","山本 健太","山本","健太"
"003","佐藤 昇","佐藤","昇"
"004","太田 隆","太田","隆"

文字列中の位置を知りたい

index()

index("文字列", "部分文字列")

$ awk 'BEGIN{print index("2021-03-26 00:00:00", " ")}'
11

文字列中の位置から部分文字列を取り出す

substr()

substr("文字列", 位置)
文字列から指定位置以降を取り出す

$ awk 'BEGIN{print substr("080-123-9999", 5)}'
123-9999

正規表現にマッチした位置を知りたい

match()

match("文字列", /正規表現/)
取り出しは RSTART, RLENGTH を利用する。
RSTART→マッチした文字の位置
RLENGTH→マッチした文字数

$ awk 'BEGIN{match("2021-03-26 00:00:00", / /); \
print RSTART, RLENGTH }'
11 1
$ echo "2021-03-26 00:00:00" | awk '{match($0, / /); \
print RSTART, RLENGTH; \
print substr($0, RSTART+1) \
}'
11 1
00:00:00

文字列置換

sedコマンドを使う

$ echo "2021-03-26 00:00:00" | sed 's/ /T/'
2021-03-26T00:00:00
$ echo "2021-03-26 00:00:00" | sed 's/\([1-2][0-9]\{3\}[-]\)\(.*\)/\2/'
03-26 00:00:00
$ echo "2021-03-26 00:00:00" | sed 's/\([1-2][0-9]\{3\}[-][01][1-9][-][0-3][0-9]\)\(.*\)/\2/'
 00:00:00
$ echo "2021-03-26 00:00:00" | sed 's/\([12][0-9]\{3\}[-][01][1-9][-][0-3][0-9]\)\( \)\([0-2][0-9]:[0-5][0-9]\)\(.*\)/\1T\3Z/'
2021-03-26T00:00Z
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