プロセスを grep するときに grep プロセス自身を結果から除外する Tip とその理屈
プロセスの grep
を業務柄よくやるのですが, grep
自身のプロセスが引っかかるのがきれいじゃなくて気になっていました*1。
(↓の grep --color=auto sshd
の部分)
kangetsu@ubuntu18:~ $ ps auxf | grep sshd root 641 0.0 0.3 72308 6324 ? Ss 01:27 0:00 /usr/sbin/sshd -D root 4524 0.0 0.3 107996 7092 ? Ss 03:44 0:00 \_ sshd: kangetsu [priv] kangetsu 4557 0.0 0.2 108360 5064 ? S 03:44 0:00 | \_ sshd: kangetsu@pts/0 kangetsu 4952 0.0 0.0 13144 1016 pts/0 S+ 04:19 0:00 | \_ grep --color=auto sshd root 4527 0.0 0.3 108000 7088 ? Ss 03:44 0:00 \_ sshd: kangetsu [priv] kangetsu 4576 0.0 0.1 108000 3624 ? S 03:44 0:00 | \_ sshd: kangetsu@notty root 4635 0.0 0.3 107996 7080 ? Ss 03:57 0:00 \_ sshd: kangetsu [priv] kangetsu 4656 1.7 0.2 108360 5080 ? S 03:57 0:22 | \_ sshd: kangetsu@pts/1 root 4639 0.0 0.3 108000 7084 ? Ss 03:57 0:00 \_ sshd: kangetsu [priv] kangetsu 4690 0.0 0.1 108000 3512 ? S 03:57 0:00 \_ sshd: kangetsu@notty kangetsu@ubuntu18:~
はじめは | grep -vw grep
などで除外してましたが, []
を使うというもっとスマートな方法がありました。
その解説をした記事は複数あるのですが, 読んだだけではピンとこなかったので自分の納得のために理屈をまとめたのがこの記事です。
TL;DR
grep
の引数の文字列の先頭を[]
で囲めば OK[]
で囲む文字は先頭でなくても OK, 複数でも OK (やる意味はない)- e.g.,
ps auxf | grep [h]oge
- GNU grep で確認, 他は未確認
環境情報
kangetsu@ubuntu18:~ $ dpkg -l | grep grep ii grep 3.1-2build1 amd64 GNU grep, egrep and fgrep kangetsu@ubuntu18:~
プロセス grep 時に grep プロセス自身を除外する方法
検索すればいろいろ記事があるので結論から書くと, grep
したい文字列の先頭文字を []
で囲めば OK です。
以下実験結果。
kangetsu@ubuntu18:~ $ ps auxf | grep yes kangetsu 4701 0.0 0.0 13144 1108 pts/0 S+ 17:09 0:00 | \_ grep --color=auto yes kangetsu 4699 29.0 0.0 6184 784 pts/1 R+ 17:09 0:01 | \_ yes kangetsu@ubuntu18:~ $ ps auxf | grep [y]es kangetsu 4699 29.7 0.0 6184 784 pts/1 R+ 17:09 0:03 | \_ yes kangetsu@ubuntu18:~
yes
コマンドを grep
していますが, []
を使った方では grep
プロセスは検索に引っ掛かっていません。
きれいですね。
[] で grep プロセス自身を除外できる理由
なぜ []
の利用でこの結果になるかというと, (少なくとも) GNU grep では標準正規表現を利用できるためです。
grep(1) の man を見ます。
[...] REGULAR EXPRESSIONS [...] grep understands three different versions of regular expression syntax: “basic” (BRE), “extended” (ERE) and “perl” (PCRE). In GNU grep there is no difference in available functionality between basic and extended syntaxes. In other implementations, basic regular expressions are less powerful. The following description applies to extended regular expressions; differences for basic regular expressions are summarized afterwards. Perl-compatible regular expressions give additional functionality, and are documented in pcresyntax(3) and pcrepattern(3), but work only if PCRE is available in the system. [...]
grep understands three different versions of regular expression syntax: “basic” (BRE), “extended” (ERE) and “perl” (PCRE). In GNU grep there is no difference in available functionality between basic and extended syntaxes.
とあります。
つまり, 例えば GNU grep で ps auxf | grep [y]es
としたとき, grep
の引数である [y]es
は標準正規表現と解釈されます。
[]
は標準正規表現で「[]
内の任意の一文字」を意味するので, [y]es
と yes
はマッチします。
[]
の説明については, 同じく grep(1) の man に下記記述があります。
[...]
Character Classes and Bracket Expressions
A bracket expression is a list of characters enclosed by [ and ]. It matches any single character in that list; if the first character of
the list is the caret ^ then it matches any character not in the list. For example, the regular expression [0123456789] matches any single
digit.
[...]
仮にこれが fes
や es
だとマッチしません。
kangetsu@ubuntu18:~ $ echo fes es | grep [y]es kangetsu@ubuntu18:~
以上から, grep
の引数の検索パターンに []
を使うことで, 次の理由で grep
プロセス自身を検索結果から除外できます。
[]
を使わないgrep
でgrep
プロセス自身が引っかかるのは,grep
の引数のパターン文字列が検索パターンに合致するため- e.g.,
ps auxf | grep yes
ならgrep yes
にyes
が含まれるので引っかかる
- e.g.,
[]
を使うとこれを回避できるのは,grep [y]es
のプロセス自身にはyes
という文字列は含まれないため ([y]es
であってyes
ではない)
実際, 別シェルで grep [y]es
を流し続けていると, ps
したときには []
付きで表示されています。
kangetsu@ubuntu18:~ $ ps auxf | grep grep kangetsu 5343 0.0 0.0 13144 1096 pts/0 S+ 06:55 0:00 | \_ grep --color=auto grep kangetsu 5321 0.0 0.0 13144 1040 pts/1 S+ 06:55 0:00 | \_ grep --color=auto [y]es - kangetsu@ubuntu18:~
なお, わかりやすさのために grep
の引数の先頭文字を []
で囲みましたが, 理屈からすれば次の様に []
で囲む文字はどこでも OK です。
kangetsu@ubuntu18:~ $ ps auxf | grep y[e]s kangetsu 5182 28.1 0.0 6184 792 pts/1 R+ 06:35 0:02 | \_ yes kangetsu@ubuntu18:~ $ ps auxf | grep y[e][s] kangetsu 5182 27.1 0.0 6184 792 pts/1 R+ 06:35 0:02 | \_ yes kangetsu@ubuntu18:~ $ ps auxf | grep ye[s] kangetsu 5182 27.2 0.0 6184 792 pts/1 R+ 06:35 0:03 | \_ yes kangetsu@ubuntu18:~
おまけ
[y]es のような文字列を検索したい
そんなニーズはないと思いますが, [y]es
で検索している grep
プロセスにマッチさせたい場合は次の様に [
]
をエスケープして引用符で囲めばできます。
kangetsu@ubuntu18:~ $ ps auxf | grep "\[y\]es" kangetsu 5419 0.0 0.0 13144 1084 pts/1 S+ 07:02 0:00 | \_ grep --color=auto [y]es - kangetsu@ubuntu18:~
エスケープするだけだとエスケープなしと同じ結果になるのは, 以下のあたりが原因でしょうか。
kangetsu@ubuntu18:~ $ ps auxf | grep \[y\]es kangetsu@ubuntu18:~
[...] Any meta-character with special meaning may be quoted by preceding it with a backslash. [...]
標準正規表現と拡張正規表現 (-E オプション) の違い
grep(1) の man には以下の記述があります。
[...]
Basic vs Extended Regular Expressions
In basic regular expressions the meta-characters ?, +, {, |, (, and ) lose their special meaning; instead use the backslashed versions \?,
\+, \{, \|, \(, and \).
[...]
つまり, -E
オプションを使わなくても OR 検索などはできます (知らなかった......)。
通常, 以下の様に -E
なしで OR 検索をしようとすると意図した結果になりません。
kangetsu@ubuntu18:~ $ ps auxf | grep "[y]es|[s]shd" kangetsu@ubuntu18:~
ところが, これら特殊文字を \
でエスケープしてあげることで, 標準正規表現でも OR 検索などできます。
kangetsu@ubuntu18:~ $ ps auxf | grep "[y]es\|[s]shd" root 641 0.0 0.3 72308 6324 ? Ss 02:49 0:00 /usr/sbin/sshd -D root 4524 0.0 0.3 107996 7092 ? Ss 05:06 0:00 \_ sshd: kangetsu [priv] kangetsu 4557 0.0 0.2 108360 5064 ? S 05:06 0:01 | \_ sshd: kangetsu@pts/0 root 4527 0.0 0.3 108000 7088 ? Ss 05:06 0:00 \_ sshd: kangetsu [priv] kangetsu 4576 0.0 0.1 108000 3624 ? S 05:06 0:00 | \_ sshd: kangetsu@notty root 4635 0.0 0.3 107996 7080 ? Ss 05:20 0:00 \_ sshd: kangetsu [priv] kangetsu 4656 3.6 0.2 108360 5080 ? R 05:20 4:59 | \_ sshd: kangetsu@pts/1 kangetsu 5556 29.0 0.0 6184 820 pts/1 R+ 07:36 0:06 | \_ yes root 4639 0.0 0.3 108000 7084 ? Ss 05:20 0:00 \_ sshd: kangetsu [priv] kangetsu 4690 0.0 0.1 108000 3512 ? S 05:20 0:00 \_ sshd: kangetsu@notty kangetsu@ubuntu18:~
ただ, エスケープが多いと可読性が悪いので, 素直に -E
を使うのがよいと思います。
kangetsu@ubuntu18:~ $ ps auxf | grep -E "[y]es|[s]shd" root 641 0.0 0.3 72308 6324 ? Ss 02:49 0:00 /usr/sbin/sshd -D root 4524 0.0 0.3 107996 7092 ? Ss 05:06 0:00 \_ sshd: kangetsu [priv] kangetsu 4557 0.0 0.2 108360 5064 ? S 05:06 0:01 | \_ sshd: kangetsu@pts/0 root 4527 0.0 0.3 108000 7088 ? Ss 05:06 0:00 \_ sshd: kangetsu [priv] kangetsu 4576 0.0 0.1 108000 3624 ? S 05:06 0:00 | \_ sshd: kangetsu@notty root 4635 0.0 0.3 107996 7080 ? Ss 05:20 0:00 \_ sshd: kangetsu [priv] kangetsu 4656 3.6 0.2 108360 5080 ? R 05:20 5:04 | \_ sshd: kangetsu@pts/1 kangetsu 5556 28.8 0.0 6184 820 pts/1 R+ 07:36 0:12 | \_ yes root 4639 0.0 0.3 108000 7084 ? Ss 05:20 0:00 \_ sshd: kangetsu [priv] kangetsu 4690 0.0 0.1 108000 3512 ? S 05:20 0:00 \_ sshd: kangetsu@notty kangetsu@ubuntu18:~
egrep, fgrep, rgrep は deprecated
わたしは使ったことないですが, egrep
, fgrep
, rgrep
は後方互換性のためにあるだけで deprecated らしいですね。
[...] In addition, the variant programs egrep, fgrep and rgrep are the same as grep -E, grep -F, and grep -r, respectively. These variants are deprecated, but are provided for backward compatibility. [...]
*1:最近は ps たくさん打ったので感覚が麻痺した