パイプ大好き

日々良く使っていそうなパイプによる連結。「パイプはモナド！！」とか言いたい方は是非[こちら](https://www.google.com/search?q=%E3%83%91%E3%82%A4%E3%83%97+%E3%83%A2%E3%83%8A%E3%83%89)へ。

## 行数を数えたい

単純に行数を数えたい場合は `wc -l` で十分です。`wc` は word count で、`-l` は line number を教えろという意味です。

```shell-session
$ netstat -tn | wc -l
34
```

でも何だか味気ないです。exact match でその行がどれくらいあるの? みたいなのが知りたい場合には `sort` と `uniq` を併せて使います。

```shell-session
$ netstat -tn | awk '{print $6}' | sort | uniq | wc -l
5
```

`uniq` は連続した重複行を 1 行にまとめてくれるので、`sort` を前にかまします。`uniq` に `-c` を付けるとその重複行が何行あったか教えてくれて便利です。

```shell-session
$ netstat -tn | awk '{print $6}' | sort | uniq -c
      1
      1 CLOSE_WAIT
     32 ESTABLISHED
      1 TIME_WAIT
      1 状態
```

`sort` は `-n` で文字列ではなく数字として sort、`-r` で逆順で sort してくれるので、最後にくっつけるとものによっては見やすくなります。

```shell-session
$ netstat -tn | awk '{print $6}' | sort | uniq -c | sort -nr
     31 ESTABLISHED
      1 状態
      1 TIME_WAIT
      1 CLOSE_WAIT
      1
```

## 欲しい行だけ抜き出す

`grep` とか `egrep` とか使えばいいですね。圧縮展開が面倒だ、というときには `zgrep` とか `zegrep` とかもあります。あんまり option 覚えていませんが、良く(?)使うのは

|option|説明|
|:--:|:--|
|`-i`|大文字小文字区別しない|
|`-v`|マッチする行以外を表示|
|`-a`|ascii として grep (制御文字入っちゃってるときとか)|
|`-h`|複数ファイル指定したとき行頭にファイル名を入れない|

少ないな、覚えてるの。表組する程でもないかもしれない。

## 行のうち欲しい部分だけ抜き出す

上にも出てるけど手軽なのは `awk` で `$6` とかしてスペースとか区切りの文字を抜き出してしまう方法。若しくは `cut` で `-c` で抜き出す column を指定しても良い。

```shell-session
$ sudo cut -c -9 /var/log/messages* | uniq -c | sort -n | tail
      9 Mar 29 20
      9 Mar 30 02
     10 Mar  9 08
     10 Mar 18 01
     10 Mar 20 01
     17 Mar 30 00
     27 Mar 12 01
    225 Mar 29 19
    351 Mar 13 07
   9118 Mar 18 23
```

ちなみに、3/18 の 23:43 は OOM killer が走っていました。

## 気軽に Perl に手を出す

`awk` 力が高かったり `grep` 力が高かったりすると色々できそうですが、私の場合貧弱なので直ぐに Perl に手を出します。

`perl` は `-n` で引数で指定されたファイルや標準入力の各行を `$_` という変数に入れて各行を処理してくれます。処理内容は `-e` でしてあげれば ok です。

```shell-session
$ netstat -tn | perl -ne '@F=split(" "); print $F[5]."\n";' | sort | uniq -c
      1
      2 CLOSE_WAIT
     28 ESTABLISHED
      1 TIME_WAIT
      1 状態
```

これは `awk` と同じことをやってるんですが、`-a` を付けると `awk` っぽく振る舞ってくれるようになり `@F` の定義すら必要なくなります。

```shell-session
$ netstat -tn | perl -a -ne 'print $F[5]."\n";' | sort | uniq -c
      1
      2 CLOSE_WAIT
     28 ESTABLISHED
      1 TIME_WAIT
      1 状態
```

また、`-F` で区切り文字も色々変えれます。`-F,` とかすればお手軽な CSV であれば読めます。で、これでもぉ立派な Perl なので、"Foreign Address" が 443 port のものの "State" だけ出力、というのも簡単にできます。

```shell-session
$ netstat -tn | perl -ane 'print $F[5]."\n" if $F[4] =~ /:443/;' | sort | uniq -c
      6 ESTABLISHED
      1 TIME_WAIT
```

正規表現さえ使えてしまうと、例えば URL っぽいものを全部引っこ抜くというのも簡単です。

```shell-session
$ curl -s http://www.google.com/ | perl -ne 'while (s/href="(http[^"]+)"//) { print $1."\n" }' | less
http://www.google.com/imghp?hl=en&tab=wi
http://maps.google.com/maps?hl=en&tab=wl
https://play.google.com/?hl=en&tab=w8
http://www.youtube.com/?tab=w1
http://news.google.com/nwshp?hl=en&tab=wn
https://mail.google.com/mail/?tab=wm
https://drive.google.com/?tab=wo
http://www.google.com/intl/en/options/
http://www.google.com/history/optout?hl=en
https://accounts.google.com/ServiceLogin?hl=en&continue=http://www.google.com/
https://plus.google.com/116899029375914044550
```

## 足し算したい

`awk` を使った場合は `END` を使うとファイル読み込みの最後の処理を指示できます。`ps aux` の結果から RSS の合計を得るには

```shell-session
$ ps aux  | awk '{s+=$6} END {print s}'
6787580
```

`s` が自動的に 0 に初期化されて後は足されていきます。

## 繰り返す

`zsh` を全く使いこなしていないので未だに `for` 文を書いています。`bash` すら使いこなしていません。

```shell-session
for i in `seq -w 2 3 20`; do echo $i; done
02
05
08
11
14
17
20
```

これは 2 から 20 まで 3 ずつ増やして 0 詰の等幅で表示しています。echo を `wget` にしたりすると連番ゲットだぜ的なものになります。

```shell-session
$ for i in `seq -w 2 3 20`; do wget http://www.example.com/$i.png; done
```

これは Perl で URL っぽいものを引っこ抜き続けるのも同じです。\`\` で囲まれてる部分がだんだん長くなってくると、残念な気分が増してきます。`mysql` とか入れないようにしましょう。

## 再帰的に directory を掘る

未だに `ack` や `ag` を使っていない老害です。`find` と `xargs` を使っています。`zsh` があればここにあることは不要なはずです。あんまり参考にせず `zsh` とか使いこなしましょう。

近頃の `find` は何も引数つけないと `-print` と同じでファイルなどの名前を表示してくれます。GNU と FreeBSD とかの違いかもしれません。

```shell-session
find /usr/lib/python2.7 | head -n 5
/usr/lib/python2.7
/usr/lib/python2.7/nntplib.pyc
/usr/lib/python2.7/dbhash.py
/usr/lib/python2.7/inspect.pyc
/usr/lib/python2.7/textwrap.pyc
```

'-name' すると名前を絞ってくれます。例えば `*py` にマッチするものだけなら、

```shell-session
find /usr/lib/python2.7 -name '*py'| head -n 5
/usr/lib/python2.7/dbhash.py
/usr/lib/python2.7/_weakrefset.py
/usr/lib/python2.7/commands.py
/usr/lib/python2.7/mailbox.py
/usr/lib/python2.7/os.py
```

で、`xargs` で出てきた出力結果ファイルに一斉にコマンドを実行します

```shell-session
$ find /usr/lib/python2.6/site-packages/botocore -name '*py' | xargs egrep 'request\.'  | less
/usr/lib/python2.6/site-packages/botocore/operation.py:        in a request.
/usr/lib/python2.6/site-packages/botocore/awsrequest.py:    """Represents a prepared request.
/usr/lib/python2.6/site-packages/botocore/response.py:        # the request.
/usr/lib/python2.6/site-packages/botocore/response.py:    if not http_response.request.method == 'HEAD':
/usr/lib/python2.6/site-packages/botocore/retryhandler.py:            to send the request.
(snip)
```

これは、指定したフォルダ以下の `py` で終わるファイルで `request` という変数の attribute を参照している部分を探していますね。何したかったんだろう。。。

`find` と `xargs` の組み合わせは、ファイル名に space が入ってたりすると悲惨なことになるので `find` の `-print0` と `xargs` の `-0` を組み合わせる必要がありますが、まぁ、ぐぐってみて下さい。

`find` は `-type` でファイルタイプが指定できますが大抵 `-type f` で regular file に限定する程度でしょう。`-mtime -3` とかで過去 3 日以内に変更されたファイル、なども選べるので、`/var/log` で作業するときには少し便利かもしれません。

## 証明書をゲットしたい

`www.google.com` のサーバ証明書だけ欲しい場合には以下で `google.pem` に保存できます。

```shell-session
$ openssl s_client -connect www.google.com:443 < /dev/null | openssl x509 > google.pem
```

中身にしか興味が無い場合には、さっさと parse しちゃいます。

```shell-session
$ openssl s_client -connect www.google.com:443 < /dev/null | openssl x509 -noout -text | less
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 8151063296760904526 (0x711e64e1d9287b4e)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, O=Google Inc, CN=Google Internet Authority G2
        Validity
(snip)
```

負荷分散されてる場合とか、一遍に調べたい場合には `for` で回しちゃいましょう。`openssl` の `-servername` は、対応してたら使えるといいですね!!

```shell-session
$ for i in `dig +short www.google.com`; do openssl s_client -servername www.google.com -connect $i:443 < /dev/null > $i.pem;done
$ ls
173.194.33.48.pem  173.194.33.49.pem  173.194.33.50.pem  173.194.33.51.pem  173.194.33.52.pem
```

## JSON が返ってくるんですけど

`perl` でよければ URL っぽいもの引っこ抜く奴で正規表現で頑張れます。あと、`json_pp` ってコマンドもあって、これ使うと pretty print してくれるので `grep` とかできますね。

`python` 使うと `json` モジュールを呼び出して parse できます。

```shell-session
$ python -m json.tools < cfn.template
```

まぁ、素直に [jq](http://stedolan.github.io/jq/) 使え、という話な気がしてきます。