awkに関する世界観です。
awkは昔からあるUNIXのスクリプト言語で、空白やタブで区切られたテキストに対してスクリプト処理をかけることができる。
特に、特定のカラムを表示させたい時は、awkを使うと便利です。
awk '{ print $3 }'
とすれば、空白やタブで区切られた3列目のカラムだけを出力できます。
計算や比較演算もできるため、フィルターで使う際に簡単なスクリプト処理(たとえば3列目のファイルサイズの数値が3MB以上のものだけを出力するなど)もできます。
awk '$3 >= 3 { print }'
awkでは、
条件式 { コマンド処理 }
という書式でスクリプトを与える。条件式がその行にマッチした場合、コマンド処理(たとえばprint)が実行される。
一行の全体は$0、行の中の空白で区切られたひとつひとつの部分(フィールド)は$1や$2などとし、条件式は検索文字列や正規表現とのマッチ、あるいは>=などの比較演算を行える。
printは、与えられた文字列や値などを表示するawkの内部コマンド。print $1とすれば1番目のフィールドを、print $0(あるいは単にprint)とすれば与えられた文字列全体を表示する。
たとえば、正規表現のマッチをする場合は、
/ho*ge/ { print }
となる。あるいは、3番目のフィールドだけに正規表現をマッチさせる場合は、
$3 ~ /ho*ge/ { print }
となる。またコマンド処理(アクション)の中にはprintのような内部コマンドだけでなく、if文やfor文やwhile文、演算子や変数や配列なども使える。
以下は参考文献。
2024.04.20
2024.05.22編集
応用例として、2列目に数字のデータがある時にそれを<p>~</p>を付加して出力したいなら、
$2 ~ /^[0-9]+$/ { print "<p>" $2 "</p>" }
のようになる。
2024.05.22
awkで区切り文字を空白文字から別の文字に変更したい場合は、awkを実行する際にオプション-Fを指定するか、スクリプトの中で最初に実行されるBEGIN {}の中でビルトイン変数FSを指定します。
2024.05.22
以下はawkの実例として参考になるページ。
以下の英語のawkのチュートリアルは、awkの基本機能を知る上でとても参考になります。
実際のところ、UNIXではgrep, sed, awkなどを使うことで、何でもかんでも一行で便利かつ効率的に操作することができます。
後日注記:awkは、システム管理をする上で、形式的な自動処理をさせるためにとても活用できます。複雑になってくると、「このawkがやっていることは一体なんなのか」という感じになってきますが、Perlのような高度なプログラミング言語を使わなくても、awkだけでできることはたくさんあります。
2023.02.19編集
grep, sed, awkは、ワンライナーでも使えますが、シェルスクリプトにすると真価を発揮します。
簡単なテキスト定型処理は、さまざまなUNIXコマンドとgrep, sed, awkを組み合わせて、シェルスクリプトにすると、UNIXでは自動的に人間の手間いらずで行うことができます。
ただし、処理が複雑になってくると、それらだけでは対応が難しくなることもあります。そのような時はPerl(あるいは今で言えばPythonやRuby)を使いましょう。特に、Perlはawkを参考にしてデザインされた言語であり、「Perlはawkの発展形」であると言えます。
後日注記:Perlはawkやシェルスクリプトの発展形であるため、サブルーチンの引数を$_[0]や$_[1]で表したり、正規表現へのマッチを「=~」で行うなど、awkととてもよく似ています。awkやシェルスクリプトを触ったことがあれば、Perlの文法表記の意味がよく分かります。
Bash(シェルスクリプト)やPerlも参照のこと。
2024.05.22-23
grep, sed, awkのようなUNIXのフィルター整形コマンドや、Perlのようなプログラミング言語は、UNIXのコマンドとコマンドを繋ぐ「グルー言語」としての利用がよくされます。
グルーとは「のり」のことで、コマンドとコマンドを結合するための「接着剤」の役割を果たします。
UNIXにおいて、コマンドの出力の情報を使って、別のコマンドの入力にパイプとして渡したい、ということはよくあります。
特に、たとえばls -lのように、UNIXのコマンドは詳細な情報を出力してくれる便利なコマンドがあります。あるいは、プロセスが依存する共有ライブラリであれば、lddコマンドやldconfigコマンドもよく使います。そのような詳細なコマンドの情報に基づいて(情報はひとつだけではなく、ファイル名やファイスパスやプロセス名などのように複数である場合も多い)、目視と手作業でコマンドを入力するのではなく、自動的にその情報をそのまま与えてコマンドを実行できれば、楽ができます。
ですが、そのようなコマンドの出力は、そのままパイプで別のコマンドに渡すことができるとは限りません。ls -lの情報はldd/ldconfigのような情報には、関係のない行や文字列もたくさん含まれていますし、その時必要な情報は特定のカラム(列)の中の情報であることが多く、そして別のコマンドに渡す際にはそのために形式を加工して整形しなければならない場合もあります。
そのような時に、grep, sed, awkのようなフィルター整形コマンドや、Perlのようなグルー言語は、大いに活用できます。グルー言語をまるで接着剤のように、あるコマンドの出力を別のコマンドの入力として与える形に適した、相応しい形式に抽出・加工・変換する、ということができるのです。
たとえば、ls -lで表示したディレクトリの中のファイル一覧の詳細情報から、grepで特定の行を選択し、awkで特定の列を選択して条件で絞り込み、sedでその形式を変換し、その文字列情報をxargsを用いることでそれぞれの行ごとに実際に処理を行うコマンド(たとえばmvやcp)に渡す、ということができます。
より具体的に言えば、ls -lで詳細表示した出力内容から、*.htmlと*.jsと*.cssのファイル名だけをgrepで抽出し(grepは-eでOR検索ができる)、その中からawkでファイルサイズが一定サイズ以上のものだけを抽出した上でファイル名の列だけを取り出し(もし更新日時で最近更新されたものだけを抽出する場合はls -lでなくfindコマンドの-mtimeオプションを使うこともできる)、その上でそれをxargsを使ってcpコマンドでコピーし、一時的なディレクトリを意味するtmpディレクトリにすべてコピーすることで、「サイズの大きなファイルだけを確認するためにtmpディレクトリに移す」ということができます。
これを総合すると、
ls -l | grep -e ".html" -e ".js" -e ".css" | awk '$5 >= 100000 { print $9 }' | xargs -I {} cp {} ~/tmp
となります。xargsのところにある{}の部分にはコピー元ファイルの名前が入ります(xargsの-Iオプションで指定する)。以下は参考文献。
また、このようなことをやっている記事はネット上にいくらでもあるので、参照してください。以下のリンク先はそのような例の掲載されているページや書籍です。
2024.05.23
2024.05.25編集
awkは、ある種の簡易的なパーサーとして使うこともできます。
たとえば、awkの-Fオプションなどで区切り文字を「,」に変えることで、CSV用の簡易的なパーサーとして使うこともできます。区切り文字をビルトイン変数で変える場合は、入力用の区切り文字は「FS」で、出力用の区切り文字は「OFS」で変えられます。
ですが、awkにも限界はあるので、本当にデータファイル(特にログファイルなど)を本格的に解析したい場合はPerlを使うことになります。awkの場合、パース自体は簡単でもその後の応用的な技術を作ることが難しいので、最初からPerlで解析したほうが楽です。Perlを使う場合、split()を使うことで区切り文字によって文字列をリストに分割でき、join()を使うことで連結できます。awkほど単純ではありませんが、このほうが柔軟かつ自由度は高いです。
2024.05.23
以下の書籍が参考になります。