よく使う 9 つの便利なシェル芸

  • 1320
    いいね
  • 5
    コメント

シェル芸・ブリンこと @b4b4r07 です。

巷ではシェル芸なるものが流行っている。

マウスも使わず、ソースコードも残さず、GUI ツールを立ち上げる間もなく、あらゆる調査・計算・テキスト処理を CLI 端末へのコマンド入力一撃で終わらすこと。

— シェル芸の定義バージョン 1.0

いわばシェルスクリプトとは逆の考え方で、コマンドラインから書き捨てのワンライナーひとつで処理しようというもの。サッとデータをフィルタして眺めたり、テキストファイルのデータ加工をしたり、誰かに示したりするときに重宝する。これができてこそシェルスクリプトも柔軟にかけるようになるだろうし、何よりカッコいいし気持ちいい。

シェル芸のコツはフィルタコマンドをよく知り、使いこなすこと。繋げること。次のデータ入力を考えたフィルタをかけること、にあると思っている。

シェル芸でよく使われるフィルタコマンドはこれだ(もちろんこれ以外にも)。

  • head
  • tail
  • sort
  • uniq
  • tac
  • cut
  • wc
  • paste
  • join
  • xargs
  • sed
  • awk

五万とある UNIX コマンドのうちこれだけとプラス少々を覚えればできるようになる。そう思えば簡単そうではなかろうか?

今回はシェル芸でよく使われるイディオム(ひとまとまりの意味)を紹介していく。イディオムなので、それ自体ではシェル芸には成り得ない。これらのイディオムを組み合わせてシェル芸にしていく。

また、AWK(オーク) については記述量が多く(AWK)なるため、以下の別記事にしました。

シェル芸のイディオム

sort | uniq -c | sort -nr

意味: データの出現語をランキングする

おそらく最頻出なイディオム。

最初の sortuniq するための事前処理で、uniq -c は重複した行をカウントする。最後の sort -nr では、カウント済みの入力をそのカウント数でソートして、数値として扱うように(-n)指示している。というのも 1, 10, 2, 3, 4, … となるのを防ぐのに数値であることを明示している。

grep -c ""

意味: 行数をカウント する

シェル芸は行指向につないでいく芸当。通常、行数カウントは wc -l で事足りるが、シェル芸では grep -c "" 。なぜなら、前者では余計な空白が交じる。

$ cat file | wc -l
    34

これらの空白のそぎ落としの処理は面倒だし、他の処理で横に長くなるのでこういった余計な手間は避けたい。grep -c "" ではそのような手間はないのでシェル芸にはもってこい。

$ cat file | grep -c ""
34

head -n 3 | tail -n 1

意味: 特定行 1 行を抜き出す

あるファイルの 123 行目を抜き出したい、なんていうシチュエーションは少ないが、何らかのシェル芸で既に加工された標準出力に対し、3 行目だけ抜き出して加工したい、なんて局面なら往々にしてある。

$ seq 1 10 | head -n 3 | tail -n 1
3

これを応用すれば、3 行目から 6 行目を抜き出す、なんてこともできる。

この記事では AWK を使った処理を意図的に避けてきた。sedawk たまに perlruby はシェル芸で使われることが多い。この headtail の組み合わせについても awk 'NR==3' で代用できる。しかし、これら LL(sed はちょっと違うが)を説明に入れだすと、とても長くなると判断し避けていた。が、awk に関しては(シェル芸では)よく使われるし、テキスト処理においては、とても強力な武器になり得るので追記した。どこまでの LL ワンライナーをシェル芸として認めるのか、シェル芸人さんの定義に含まれると嬉しい(個人的に perl より枯れている印象を覚える awk は含まれるとしたい)。

grep -o

意味: 特定語 word を抜き出す

通常 grep コマンドはテキストや標準出力にある word を含む行を出力する。word だけを取り出したい、といったとき、sed などによる置換で取り除くことを考えるかもしれないが、それは冗長。grep にはそれを満たすナイスなオプションが有るのだ。

$ cat /etc/passwd | grep lisa
lisa:x:504:10::/home/lisa:/bin/zsh

これだと丸々 1 行とれてしまうが、

$ cat /etc/passwd | grep -o lisa
lisa
lisa

-o でワードだけとれているのがわかる。また、ワードは行に展開されるので 3 個ワードがある場合、3 行になる。

これに grep -c "" などで行カウントをパイプで繋げたら、ワードの個数が取れるのは想像に難くない。

tac

意味: ファイル/標準出力の逆順出力をする

tac とは cat を逆さまにしたやつ(文字的にも機能的にも)だ。ファイルを(連結して)逆順出力してくれる。ただし、GNU/Linux や coreutils にしか付属しないツールなので、BSD 系のユーザ(OS X を含む)は tail -r を使う必要がある(じゃあ tail -r 一つで解決じゃんと思うかもしれないが、Linux 系にはこのオプションはない)。

この差異については、以下の記事が参考になる。

ただ、今回はシェル芸なので移植性(ファイルに保存するシェルスクリプトではない)も何も関係ないので環境に合わせて適宜変えていくだけである。

9 つもなかった

追記したい。

シェル芸のポイント

上達のポイント

いままでのイディオムを見てきて、おそらく気づいた人も多いだろうが、コマンドの仕様やオプションなどについて熟知しているとより早く柔軟に手数も少なく実行できる。man を見たり、実際に手を動かして覚えていくといいと思う。man コマンドは基本英語だがロケールできるし、ウェブ版の man もある。

高速化のポイント

よくある誤解のようなもののひとつに、パイプは処理が遅い、という認識だ。というのも、パイプ先はすべて違うプロセスが生成されてそこで実行されるからだろう。実はそれは大きな嘘で、マルチコア/メニーコアなこのご時世ではむしろ高速化されている事が多い(環境にもよる)。パイプ先で生成されるプロセスはデータが流れたときに作られるのではなく、一斉に作られ常にデータ待ちをしていて、流れてきた瞬間から逐次処理をしているからだ。

そのため、パイプの先頭のほうでボトルネックとなるような処理を挟むのは、高速化したいのなら避けるべきだ。

例えば、sort コマンド。これはすべてのファイルをメモリ上に読み込んでから処理をスタートする。その I/O 待ちの時間はファイルサイズによっては相当なボトルネックとなり得る。極力他のフィルタをかませて行数を減らしてから sort するべきだ。

試しに 594 万行(2.5 GB)のテキストファイルを対象に実験してみたところ、ソートするだけで 22 分 18 秒かかった。メモリキャッシュに載せるだけでも 5 分近く要していた。予め grep やその他フィルタで小さくしていかないと、ここで流れがストップするのがよく分かる。

最近話題のリンクを参考文献としてあげておく。

まとめ

シェル芸はフィルタ芸でもあるように、フィルタコマンドを多く知り、つなげていくことにある。これは UNIX 哲学の「フィルタとして振る舞うようにせよ」や「ひとつのことをうまくやらせる」といった考えにも合致する。これに関して、パイプの開発者 M.D.マキルロイも次のように要約している。

これが UNIX の哲学である。一つのことを行い、またそれをうまくやるプログラムを書け。協調して動くプログラムを書け。標準入出力(テキスト・ストリーム)を扱うプログラムを書け。標準入出力は普遍的インターフェースなのだ。

— M. D. マキルロイ、UNIXの四半世紀

つまり、シェル芸(と呼ばれてなにか特別なもののように聞こえるそれ)は UNIX 哲学を体現化したようなもので、その思想に則ってテキスト処理のためにコマンドを実行しているに過ぎない。