特定の文字列を含まないものを抽出したいとき、grepで実現するには、 -v オプションを使用すればいい。-vオプションはPATTERNに合致しないもののみ抜き出す。

$ cat test.txt
1.n o tを含まない文字列
2.notを含む文字列

$ grep -v not tes
1.n o tを含まない文字列

#正規表現もOK
$ grep -v no.* tes
1.n o tを含まない文字列

-vオプションは正規表現自体の否定であり、PATTERNに合致するものを除外してくれる。

正規表現「自体」の否定ではなく、否定を表す正規表現で同じ挙動をするにはどうすればいいか。 今回は、-vオプションを使わず、正規表現のみで同じ挙動を実現する方法を考える。

まず、正規表現の書き方は【正規表現】以外と知らない特定の文字列を含まない正規表現に特定の文字列を含まない正規表現がばっちり書いてあったので、拝借。

^(?!.*abc).*$

?!は、特定の文字列を含まないことを表す正規表現だ。 .*で任意の文字列を表しているため、上記の正規表現は、任意の文字列のあとにabcを含まない文字列となる。

これをgrepコマンドで使用するには、 -P オプションを付ける必要がある。 -Pオプションは、--perl-regexpオプションと同義で、PATTERN をPerlの正規表現として扱う。

manには次のように記載されているが、今回の正規表現は問題なく使える。

PATTERN を Perl の正規表現として扱います。 きわめて実験的なものなので、 grep -P を使うと、その機能は実装されていませんという 警告が出るかもしれません。

もう一点grepコマンドで使用する際に注意する点がある。
bashでは ! (exclamation mark)がhistory expansion機能を持つ。
!の次にくる文字列から始まる一番最近使用したコマンドを自動で呼び出してくれる。

# 例
$ date
Thu Jul 21 10:32:50 UTC 2016
$ !d
date
Thu Jul 21 10:32:54 UTC 2016

そのため、正規表現部分をダブルクオーテーションではなく、シングルクオーテーションで囲まなければいけない。 ダブルクオーテーションで囲むと -bash: !.*文字列: event not found というエラーが表示されてしまう。

$ cat test.txt
1.n o tを含まない文字列
2.notを含む文字列

# OK
$ grep -P '^(?!.*not).*$' test.txt
1.n o tを含まない文字列

# NG
$ grep -P "^(?!.*not).*$" test.txt
-bash: !.*not: event not found

ではfindコマンドはどうか。
この正規表現をfindで用いようとしても上手くいかない。findの正規表現タイプにperl相当のものがないためである。 findで特定の文字列を含まないファイルを調べたいときは、grepの-vオプションと同様に、正規表現を否定するオプション !(exclamation mark)を付ければよい。 つまり、 ! -regex PATTERN となる。
※findの正規表現参考:正規表現に合致するファイル名をfindする方法

grepで否定の正規表現を使用するにも注意すべき点がたくさんあるし、findではそもそも使えないので、否定の正規表現ではなく、正規表現自体を否定するオプションを使用するのが簡単という結論になってしまった。