howto/sedとawk

一行メモ


sed

sedは入力ファイル(無ければ標準入力)に対して、各行ごとにコマンドを実行し、 編集された行を標準出力に書き出すストリームエディタである。 ホールドスペースを使うと2行以上に渡って編集することもできる。
  sed [オプション] コマンド [ファイル]
のように実行する。

基本的な例

置換 s

sed 's/foo/bar/' file.txt

各行の最初の fooを barに置換する。この時のコマンドは sである。 行に複数ある fooをすべて barに置き換えるには 's/foo/bar/g'とする。

範囲指定

sed '2,5s/foo/bar/' file.txt
sed '2s/foo/bar/' file.txt

のようにコマンドの前に範囲を指定することもでき、 2,5sで2行目から5行目を、2sで2行目のみを置換することができる。 2,$sとすると2行目から最後の行までを指定できる。

sed '/chap1/,/chap2/s/foo/bar/' file.txt

数字以外にも /chap1/,/chap2/とすると chap1を含む行から chap2を含む行までという範囲指定もできる。

sed '/chap1/s/foo/bar/' file.txt

の場合は chap1を含む行のみを指定して、コマンドsを実行している。

範囲指定には '2,5!s/foo/bar/' や '/chap1/!s/foo/bar/'のように、 2行目から5行目以外や chap1を含む行以外を指定することもできる。

複数のコマンド

コマンドは ;で区切って複数与えることができる。そのコマンドは並んでいる順番で処理される。 { }で括ることもできる。

sed '2,5s/chap/bar/; 2,5s/bar/foo/' file.txt
sed '2,5{s/chap/bar/; s/bar/foo/}' file.txt

タブの利用

sed '2,5{s/,/\t/g; s/calc/foo/}'

のようにタブ文字に関する置換は 's/,/\t/g' のように \tを使う。 なお、bashの場合は Ctrl-v Tab でタブの文字コードを入力でき、\tの代わりに使用できる。

正規表現

検索文字列としては正規表現も利用可能である。 行頭と行末の一致はそれぞれ ^と $である。

例えば、CPUがサポートしている機能の一覧を作りたいときは(表示コマンドpも参照のこと)

cat /proc/cpuinfo | sed -n '/^flags.*: /p' | sed -n 1p \
  | sed 's/^flags.*://; s/ /\n/g' | sort

\( \)で検索文字列を括ると、括った順番に \1や \2で置換後の文字列として利用できる。

cat /proc/cpuinfo | grep address | sed 's/^\(.*\) \(.*\)\t:/\2 \1\t:/'

は address sizes を sizes addressに置き換えている。

表示 p

指定した範囲だけを表示するには

sed -n '2,5p' file.txt

とする。 通常、1行ずつ読み込んでコマンド実行した結果の行を出力するが、-nオプションを付けると、 明示的に表示コマンドpを使わない限り表示しない。 従って、-nを付けておいて、2,5に対してコマンドpを実行することで2行目から5行目だけを表示することができる。

追加 a, i

sed '2aNEW-LINE' file.txt    #2行目にNEW-LINEという行を追加する
sed '2iNEW-LINE' file.txt    #2行目の前にNEW-LINEという行を追加する

削除 d

sed '4d' file.txt    #4行目を削除する

練習問題

(1) echo $PATH | sed 's/:/\n/g' は何をしているか?

(2) /proc/cpuinfoをもとに、CPUのモデル名と動作クロックのみを sedで取り出してファイルに保存するには?

(3) /bin/ls -l の出力をもとに sedでファイル名と時刻(または西暦)のみを表示させるには?

詳細

簡易マニュアル

入力は行ごとにパターンスペースに入れられ(末尾の改行は削除される)、コマンド処理後に、 出力される(改行は追加される)。一時的な保存場所であるホールドスペース(初期値は空)を 利用できる。-nオプション付きの場合、下記のpコマンドで出力しない限り何も出力されない。

コマンド
  s/検索文字列/置換後文字列/オプション    文字列の置換(パターンスーペース内)
         オプション
           g        置換を最後まで行う(g無いときは1回だけ行う)
         検索文字列
           .        任意の文字
           *        直前の文字の0個以上の繰り返し(.*で任意の文字列)
           ^        行頭
           $        行末
           \(...\)  括弧でくくった部分は、置換後文字列として\1,\2,...で使用できる
           \|       または
  d    削除(パターンスーペース内)
  p    出力する(パターンスペース内)、sed -n で実行した時にこの出力だけ得られる
  G    ホールドスペースの内容をパターンスペースに追加(追加の前に改行が追加される)
  h    パターンスペースの内容をホールドスペースにコピー
  g    ホールドスペースの内容をパターンスペースにコピー
  H    パターンスペースの内容をホールドスペースに追加(追加の前に改行が追加される)
  N    次の入力行をパターンスペースに追加
  b [label]    labelに分岐する。labelが無ければ末尾に分岐。
  =    行番号を出力する(改行つき)

上記コマンドの前には対象を指定できる
  /検索文字列/[!]       検索文字列を含む行を対象、!がある場合は否定の意味
  行番号[,行番号][!]    入力の行番号(から行番号)の行を対象、!がある場合は否定の意味、
                       $は最終行の行番号

;で区切ってコマンドを複数書ける。
{}でコマンドをくくって複数書ける。
例)
sed '1,/^$/d' hogefile  #1行目から、2行目以降の空行までを削除し、それ以降を出力する
sed -n '/GB/p' hogefile  #これは grep GB hogefile と同じ結果
sed -n '1,2p; /GB/{s/pot/Pot/; s/:/=\t/; p}'
sed -n '1,2{=; p}; /GB/{s/0/a/; N; N; =; p}'  #あまり意味無い例
E表現の数値を小数点以下4桁に切り捨てる例)
sed    's/\(-\?[0-9]\.\|-\?\.\)\([0-9]\{4\}\)\([0-9]*\)\([eEdD][+-]\?[0-9]\{2\}\)/\1\2\4/g'  filename
sed -r 's/(-?[0-9]\.|-?\.)([0-9]{4})([0-9]*)([eEdD][+-]?[0-9]{2})/\1\2\4/g'                  filename
filename内の継続行(バックスラッシュ'\'とこれに続く改行'\n')を削除し1行にする例)
sed ':a;N;$!ba;s/\\\n//g' filename
 (説明)
  :a で aというラベルを定義しておく。
  N で次の行をパターンスペースに追加し、
  $! で最後の行以外の行に対して、次のコマンド(ba)を実行する。ba は aラベルにジャンプする。
  最後の行のときにパターンスペース(全ての行が含まれる)に対して、置換 s/\\\n//gを行なう。結果も出力される。

[ref] https://qiita.com/gin_135/items/773fec1343a69c9f90d6
man sed も見よ

拡張正規表現 ( sed -r )

\(, \), \{, \}, \? などは (, ), {, }, ? などと表記する。 その他に使える表現が増える。

awk

awkは入力ファイル(無ければ標準入力)に対して、各行ごとにパターンに一致した場合、アクションが実行され、 標準出力に書き出すという、パターン検索・処理言語である。 sedよりも柔軟でC言語と似ている。開発者Aho, Weinberger, Kernighanの頭文字からawkと名付けられている。
  awk [オプション] プログラム [ファイル]
のように実行する。プログラムの部分は
  パターン { アクション }
の羅列である。

基本的な例

表示

awk '/foo/ { print }' file.txt

文字列fooがある行を表示する。引数無しの printだけだと行全体を表示する

awk '{ print $1 }' file.txt

1列目だけを表示する。パターンを与えていないのですべての行がアクションの対象となっている。

デフォルトで列は空白(タブやスペース)で区切られている。-Fオプションで -F, とするとカンマ区切りに変更できる。 $1, $2, $3, ...はそれぞれ1列目, 2列目, 3列目, ...を表す。$0は行を表す(行末の改行は含まない)。 NFは列の数、NRは行番号の変数として使える。

awk '{ print NR, $1, $1, "==>", NF }' file.txt

これは読み込んだ各行について、行番号、1列目、1列目、文字列==>、列の数を表示する。

ファイルサイズとファイル名を表示する例

/bin/ls -l | awk '{print $5, "\t", $9}'
/bin/ls -l | awk '{printf "%20s%20s\n", $5, $9}'

C言語と同様の組み込み関数printfを使って整形表示することもできる。

OSに登録されているユーザー名とログインシェルを表示する例

awk -F: '{print $1,"\t",$7}' /etc/passwd

算術演算

1列目の合計と平均を計算する。

awk '{sum += $1} END {print sum, sum/NR}' file.txt

変数(ここではsum)は初めてプログラムに現れた時に、自動的にゼロに初期化される。 sum += $1 は sum = sum + $1 としてもよい。C言語と同様の二項演算子は使える。 このプログラムでは、各行について {sum += $1}が実行される。 END { } は、すべての行について実行し終わったとき、最後に一度だけ実行される部分である。 各行の実行の前に、最初に一度だけ実行する BEGIN { } というものもある。 $1などや変数の値が、数値であるか文字であるかの判断は自動的にされる。

awk '{nc += length($0); nw += NF} END {print NR, nw, nc}' file.txt

このプログラムは行数、単語数、文字数をカウントして表示している。 組み込み関数lengthは文字数を与える。

awk '{nc += length($0); nw += NF} END {print nc/nw}' file.txt

は単語の平均的な文字数を計算している。 nc/nwを int(nc/nw)すれば小数点以下を切り捨てて整数値を得ることもできる。

find . -path '*.c' -printf "%p\t%s\n" | awk '{sum+=$2} END{print sum}'

はカレントディレクトリ以下の拡張子.cのファイルサイズの和を求める。

find . -path '*.c' -exec wc  {} \; | awk '{sum+=$1} END{print sum}'

はカレントディレクトリ以下の拡張子.cのファイルの行数の和を求める。 Linuxコマンドfindとwcの説明はここでは略。

フロー制御

  if ( 条件 ) 文1; else 文2
  for ( 式1; 条件; 式2 ) 文
  for ( 変数 in 配列 ) 文
  while ( 条件 ) 文
も使える。配列を使う例は下記の配列を参照のこと。 文のところは中括弧で括って { 文1; 文2; 文3 }のようにもできる。

1列目がfooという文字列のとき、2列目を出力するには

awk '{ if ( $1 == "foo" ) print $2 }' file.txt

これは

awk '$1 == "foo" { print $2 }' file.txt

と同じ結果を与える。

各行について行番号を表示したのち、列の項目をひとつずつ表示するには

awk '{ print "====", NR, "===="; \
  for ( i=1; i<=NF; i++) print $i, "<--", NF }' file.txt

配列

度数分布を作成する。binは区間の幅である。(注) 区間に0個のものは表示されないプログラムである。

awk 'BEGIN{ bin=2.0 } \
  { f[ int($1/bin) ]++ } \
  END{ for( i in f ) print i*bin, f[i] }' file.txt

単語の数をカウントする。

awk '{for (i=1; i<=NF; i++ ) num[$i]++} \
  END { for ( word in num ) print word, num[word] }' file.txt

この配列の添え字は整数ではなく文字列である(配列の添え字は $iなので i列目の文字列が添え字になっている。 いわゆる連想配列である)。

練習問題

(1) 1列目と2列目に数値が並んでいるファイルがあるとします。 2列目が最大値である行を表示する awkプログラムはどう作ることができる? (なお、最大値が複数ある場合は最初の行だけを表示するだけにする。最大値であるすべての行を表示するには?)

統計屋のためのAWK入門

http://antibayesian.hateblo.jp/entry/2014/09/15/162605


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS