部屋の隅っこで書く技術ブログ

Web系企業勤務のエンジニアリングマネージャのブログです。技術ブログと称しつつ技術にまつわる個人的な話題が多めです。

grepとsedとawkをおさらいしたので役に立ちそうなことをまとめた

f:id:expajp:20210523161307p:plain

いままでこんな状態だった

f:id:expajp:20210523160656p:plain

雰囲気で株をやっているジェネレーター

ので、標準正規表現拡張正規表現の違い、それからgrepsedawkをざっくり勉強し直しました。

せっかくなので、役に立ちそうなことと感じたことをブログに書き残しておきます。

まとまりのない記事になりましたが、僕と同じく雰囲気で使っている方の学習のきっかけになれば。

標準正規表現でできないこと

UNIXコマンドで扱える正規表現は「標準正規表現」と「拡張正規表現」の2種類あって、大抵のコマンドではデフォルトで前者、オプションの付与(grepなら-E, sedなら-r)で後者が使えます。

脳死grepを実行して「あれ、上手く動かないなあ」となることがよくあったので、以下の記事を読みました。

qiita.com

「標準正規表現ではできないこと」を覚えておけば、とりあえず迷うことがなくなるはずなのでその視点でまとめました。大きく分けて2つです。

1. 特定のメタ文字が使えない

標準正規表現では以下のメタ文字が使えません。

  • +: 1回以上の繰り返し
  • ?: 直前に記述していた文字が0回か1回現れる
  • |: いずれかがマッチ

2. ()と{}をバッククォートなしで使えない

標準正規表現(){}を使う場合、バッククォートが必要です。

以下の2つは同じ「『あ』を2回繰り返す箇所のある行を抽出する」処理を実行します。

$ grep "\(あ\)\{2\}" # 標準正規表現
$ grep -E "(あ){2}" # 拡張正規表現

grep

ファイルから正規表現に合致した箇所を抽出するツール。GNUのマニュアルを読みました。

linuxjm.osdn.jp

Macの人はGNU版を入れておくと便利

Mac標準の正規表現は機能が少ないので、HomeBrewでGNU版をインストールした上で、PATHを通しておくと便利です。

ちなみに、PATHは頭から順に探すのでGNU版のPATH/usr/local/binよりも前に記述しておかないとうまくいきません。

参考:

www.rasukarusan.com

マッチングの対象は柔軟に指定できる

  • 大文字小文字を無視
  • マッチしない行の方を抜き出す
  • 行全体のマッチのみをマッチとみなす

などがオプションで指定可能です。

文脈行の出力ができる

-Aでマッチ行の後ろ、-Bでマッチ行の前、-Cでマッチ行の前後の出力行数を指定可能です。

ちなみに、それぞれafter-context, before-context, contextに対応するので、適当にABCと決めたわけではないみたいです。

sed

Stream EDitorの略で、ストリームに対して変換をかけられるツール。

1行ずつ読み込んで処理するので巨大ファイルがメモリを埋め尽くすことはありません。

このページがよくまとまっているので参考にしました。

sed.open-code.club

Macの人はGNU版を入れておくと便利

grepに続きこちらもです。PATHを通しておきましょう。

$ brew install gnu-sed

アドレスで処理対象行を指定して、コマンドで変換をかける

$ sed s/hoge/fuga/g file.txt # hogeをfugaに置換

のように指定されているイメージですが、これは処理対象行を省略(=すべての行が対象)しているだけ。

$ sed /piyo/ s/hoge/fuga/g file.txt # piyoが含まれる行のhogeをfugaに置換

のように、最初に処理対象行を抽出する正規表現アドレス)を指定し、次に処理内容(コマンド)を指定するのがsedの標準的な書式です。

パターンスペースの概念が重要

パターンスペースとはsedの処理領域のことです。ここにファイルを1行ずつ読み込んで、それに対してコマンドを実行することで処理が進行していきます。

この流れを頭にいれることがsedを理解する上で非常に重要で、パターンスペースへの理解なくしてnコマンド・Nコマンドを使いこなすことはできません。

文字ごとに置換が可能

yコマンドを使います。どういうことかというと、

$ echo "おえういあ" | sed 'y/あいうえお/アイウエオ/'
# => オエウイア

こういうことです。これを使って1字ずらし暗号も簡単に作れます。

$ echo "めろすはげきどした" | sed 'y/あいうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよわをんがぎぐげござじずぜぞだぢづでどばびぶべぼ/いうえおかきくけこさしすせそたちつてとなにぬねのはひふへほまみむめもやゆよわをんあぎぐげござじずぜぞだぢづでどばびぶべぼが/'
# => もろせひごくばすち

現代では置換以外の用途はなさそう

他にもコマンドはあるのですが、

  • 行の出力
  • 行の削除
  • 後ろの行に指定した文字列を追加

など、ファイルを書き換えるなら普通にエディタ開いてやればええやんというものが多く、現在ではエディタらしい機能を使うことは珍しいです。

ハードウェアの進歩で「巨大ファイルを全部メモリに展開せずに処理できる」というsedのメリットはほぼ失われました。まあ、1974年からあるので仕方ない。

パイプラインの途中でフィルタをかける場合には使えますが、awkPerlのようなスクリプト言語と選択になり、これらのほうが扱いやすいんですよね。

正規表現による一括置換処理をしたい場合に使うものと割り切って問題なさそう。

awk

1977年製の老舗スクリプト言語で、grepsedの拡張として開発されたもの。sedスクリプトも一応チューリング完全ですが、こちらはもっとしっかりプログラミングができます。

テキスト処理に特化しているので、入力テキストデータありきの文法になっているし、なんならデータを渡さないと実行すらしてくれません。

マニュアルを読むのは大変そうだったので、老舗サイトの力を借りました。

www.tohoho-web.com

それから、入力データをいちいち考えるのが大変なので、ここからポケモンのデータを借りた上で、ポケモン名とタイプ名を日本語に変換して利用しました。

www.kaggle.com

入力を渡さないと実行ができない

awkコマンドは空でも良いので入力を渡さないと実行してくれません。

$ echo | awk '{ print "Hello, World!" }'
Hello, World!

入力を渡さずに実行すると、標準入力からの入力待ちになって入力を渡すたびに処理が実行されます。

$ awk '{ print "Hello, World!" }'
a # 入力
Hello, World!
a # 入力
Hello, World!
^C  # 処理の中断

sedと同じく、処理対象行を指定して処理内容を記述する

sedと違う点は

  • 処理対象行の指定が正規表現だけでなく、範囲指定が可能
  • 処理内容が豊富

の2点。

例えば、これはpokemon.csvピカチュウからポリゴンまでの行を指定して出力するスクリプトです。正規表現を使わず、かつ範囲指定をしています。

$ awk -v FS=, '$2 == "ピカチュウ", $2 == "ポリゴン" { print $0 }' pokemon.csv # ピカチュウからポリゴンまで
25,ピカチュウ,でんき,,320,35,55,40,50,50,90,1,FALSE,
26,ライチュウ,でんき,,485,60,90,55,90,80,110,1,FALSE,
...
136,ブースター,ほのお,,525,65,130,60,95,110,65,1,FALSE,
137,ポリゴン,ノーマル,,395,65,60,70,85,75,40,1,FALSE,

CSVの処理に便利

awkでは、入力データを行ごとにレコードで区切り、さらに1つのレコードをスペースで複数に区切ってフィールドにする処理がデフォルトで行われます。

レコードやフィールドへのアクセスは簡単にできるので、これらに対して処理をしていくというのが基本です。

レコードやフィールドの区切り文字は任意で指定可能なので、フィールドの区切り文字をカンマにすることでCSV処理がめっちゃ楽です。

例えば、これは区切り文字をカンマに指定した上で、「リザードン」を含む行の名前・タイプ1・タイプ2のみを出力するスクリプトです。

$ awk -v FS=, '$2 ~ /リザードン/ { printf "%s %s %s\n", $2, $3, $4  }' pokemon.csv
リザードン ほのお ひこう
メガリザードンX ほのお ドラゴン
メガリザードンY ほのお ひこう

プログラミング言語らしい文法がある

演算子、変数、配列、関数定義、分岐、ループなどが可能です。

一応、外部ライブラリで色々できる

ファイル操作やプロセス操作、数学、時間などの外部ライブラリがあるため、大規模なアプリの作成も可能です。

たぶん、別の言語でやったほうが良いですけど。

同じ処理を複数のファイルに一度に実行できるのがExcelにない強み

CSVに対する処理をしたい場合はExcel, Numbersなどの表計算ソフトを使うのが現代の常識ですが、awkを使うメリットは現代においてもまだあります。

  • 入力ファイルを複数指定できるので、一括処理が可能
  • 処理内容をスクリプト化できるので、処理の標準化および自動化が可能
  • 処理内容をファイルに保存して何度でも実行可能

まあ、まんまスクリプト言語としてのメリットなんですけどね。

ただ、CSVのようなデータを処理するのに特化しているので、テキストの前処理など余計な処理を書かなくて良いのは他のスクリプト言語にないメリットです(上記「CSVの処理に便利」を参照)。

まとめ

  • 標準正規表現でできないことを覚えておくと便利
  • grepは検索と出力を思ったより柔軟に指定可能
  • sedは現代では置換用コマンドと思って問題なさそう
  • awkCSVのようなデータを扱うのがとても便利でプログラミング言語らしい処理ができる

喉の奥にささった小骨のように気になっていた内容だったので、改めて勉強してよかったです。