AWKによるワンライナー7つ

AWKは、PythonRubyに比べれば古くさいイメージがあります。 しかし、ワンライナーとしてはAWKは強力(という話)です。

そこで、一度覚えておけば、きっと役立つ機会はおおいべしと、 AWKワンライナーのよくありそうなパターンを7つ調べてみました。

また、同時にシェルスクリプト版(sedgrepを使ったワンライナー)も併記しました。

  1. 要素を取り出してフォーマットする
  2. タブ以外の区切りを使う
  3. 正規表現にマッチした行を出力する
  4. 正規表現のマッチを置換する
  5. 整数として計算する
  6. 他のコマンドを呼び出す
  7. Schwartz変換ソート

1. 要素を取り出してフォーマットする

入力:

# 言語名 コマンド名 バージョン番号
echo -e "\
Perl\tperl\t5.14.2
Python\tpython\t2.7.3
Ruby\truby\t1.9.3p194
PHP\tphp\t5.4.6-1
AWK\tawk\t4.0.1" > /tmp/langs

ワンライナー:

# "コマンド-バージョン番号" に変換
cat /tmp/langs | awk '{ print $2 "-" $3 }'

シェルスクリプト版:

cat /tmp/langs | sed -e 's/\([^\t]*\)\t\([^\t]*\)\t\([^\t]*\)/\2-\3/'

出力:

perl-5.14.2
python-2.7.3
ruby-1.9.3p194
php-5.4.6-1
awk-4.0.1

2. タブ以外の区切りを使う

入力:

# CSVファイル
echo "\
name,price_per_100g,weight
pork,99,1200
beaf,120,1400
chicken,68,2300" > /tmp/meats.csv

ワンライナー:

# 肉の種類と重さを出力
cat /tmp/meats.csv  | awk -F',' '{ print $1 "," $3 }'

シェルスクリプト版:

cat /tmp/meats.csv  | cut -d , -f 1,3

出力:

name,weight
pork,1200
beaf,1400
chicken,2300

3. 正規表現にマッチした行を出力する

入力:

# 言語名 コマンド名 バージョン番号
echo -e "\
Perl\tperl\t5.14.2
Python\tpython\t2.7.3
Ruby\truby\t1.9.3p194
PHP\tphp\t5.4.6-1
AWK\tawk\t4.0.1" > /tmp/langs

ワンライナー:

# 言語名にrを含む言語の行を出力
cat /tmp/langs | awk -v IGNORECASE=1 '$1 ~ /r/ { print $0 }'

シェルスクリプト版:

cat /tmp/langs | grep --ignore-case -E "`echo -e '^[^\tr]*r[^\t]*\t'`"

出力:

Perl  perl    5.14.2
Ruby    ruby    1.9.3p194

4. 正規表現のマッチを置換する

入力:

echo "\
float pi(void);
int floor(float x);
pi() + bar(2.5);

void printf(char* format, ...);
printf("%s", 0);" > /tmp/code

ワンライナー:

# C言語風の宣言を、C++0x風の後置型の宣言に
cat /tmp/code | awk '{ print gensub(/([a-z]+)\s+(.*);/, "auto \\2 -> \\1;", 1, $0) }'

シェルスクリプト版:

cat /tmp/code | sed -e 's/\([a-z]\+\)[[:space:]]\+\(.*\);/auto \2 -> \1;/'

出力:

auto pi(void) -> float;
auto floor(float x) -> int;
pi() + bar(2.5);

auto printf(char* format, ...) -> void;
printf(%s, 0);

5. 整数として計算する

入力:

# 肉の種類 100gあたり価格 グラム数
echo -e "\
pork\t99\t1200
beaf\t120\t1400
chicken\t68\t2300" > /tmp/meats

ワンライナー:

cat /tmp/meats | awk '{ print $1, $2 * $3, "円"}'

シェルスクリプト版:

# 肉の種類 金額
cat /tmp/meats | while read line; do
  cols=($line)
  echo ${cols[0]} `expr ${cols[1]} '*' ${cols[2]}` 円
done

出力:

pork 118800 円
beaf 168000 円
chicken 156400 円

6. 他のコマンドを呼び出す

入力:

# 言語名 コマンド名 バージョン番号
echo -e "\
Perl\tperl\t5.14.2
Python\tpython\t2.7.3
Ruby\truby\t1.9.3p194
PHP\tphp\t5.4.6-1
AWK\tawk\t4.0.1" > /tmp/langs

ワンライナー:

# "コマンド名 コマンドのフルパス"
cat /tmp/langs | awk '{ "which " $2 | getline t; print $2, t }'

シェルスクリプト版:

for cmd in `cat /tmp/langs | cut -f 2`; do
  echo -e "$cmd" `which $cmd`
done

出力:

perl /usr/bin/perl
python /usr/bin/python
ruby /usr/bin/ruby
php /usr/bin/php
awk /usr/bin/awk

7. Schwartz変換ソート

入力:

# 言語名 コマンド名 バージョン番号
echo -e "\
Perl\tperl\t5.14.2
Python\tpython\t2.7.3
Ruby\truby\t1.9.3p194
PHP\tphp\t5.4.6-1
AWK\tawk\t4.0.1" > /tmp/langs

ワンライナー:

# メジャーバージョン - マイナーバージョン - リリースバージョン でソート
# シェルスクリプトと代わり映えしないなぁ・・・
cat /tmp/langs \
  | awk '{ print gensub(/(.*)\.(.*)\.(.*)/, "\\1\t\\2\t\\3\t", 1, $3), $0}' \
  | sort --key=1,3 \
  | awk '{ print $4 "\t" $5 "\t" $6 }'

シェルスクリプト版:

cat /tmp/langs \
  | sed -e "s/[^\t]*\t[^\t]*\t\(.*\)\.\(.*\)\.\(.*\)/\1\t\2\t\3\t\0/" \
  | sort --key=1,3 \
  | cut -f 4-

出力:

JavaScript    node    0.6.19
Ruby    ruby    1.9.3p194
Python  python  2.7.3
Perl    perl    5.14.2
PHP php 5.4.6-1