カレントディレクトリから指定したディレクトリを除外してgrepする

## はじめに

みなさん、こんにちは。ゆうたです。

今回はLinuxでファイルの中身をgrepで全件検索するときに使えそうなワンライナーを使ったので記事にしたいと思います。
以下の記事も参考になるので是非ご覧ください。
[Linuxにおいて指定パス配下に存在する任意の拡張子を持つファイルを検索する方法](https://qiita.com/torihazi/items/b86bbbca124e5993862a)
※初学者なので認識相違があればご指摘頂けますと幸いです。

## 環境
```
Python 3.12.0
Django 5.0.6
```

## 結論
以下のコマンドになります。
```find . -name "*" -type f | xargs grep -r --exclude-dir 除外ディレクトリ "キーワード" | grep -n "キーワード"```
※PC、およびサーバのリソースに負荷のかかるコマンドになるので、連投する場合はリソースを見ながら実行するか、商用のサーバで実行する場合は連投は控えて頂けると幸いです。

例　./venvディレクトリを検索対象から除外して"required"で検索
```find . -name "*" -type f | xargs grep -r --exclude-dir ./venv "required" | grep -n "required"```

## 解説
* findコマンド
ファイルやディレクトリを検索する時に使うコマンドです。

* . （カレントディレクトリ）
現在いるディレクトリを表してます。※pwdで現在位置のディレクトリを確認できます。
/(ルートディレクトリ)を指定して全件検索するとPC、もしくはサーバに負荷がかかることになるので/ディレクトリは指定しないことを強くお勧めします。

* -name
検索するキーワードを指定してます。
※今回は＊でワイルドカードを指定することで|（パイプ）で渡した全ファイルに対してキーワードによる全件検索を行います。
|（パイプ）はコマンドの橋渡しをするときに使います。

* | xargs grep -r --exclude-dir path
xargsで前のコマンドの実行結果を標準入力（プログラムに渡った値）から受け取って、次のコマンドの引き数に渡してます。
また、pathに指定したディレクトリを検索対象から除外します。

* | grep -n "required"
一つ目の|（パイプ）で除外検索した上でgrepによるキーワード検索を行います。
-nオプションでヒットした検索キーワードがファイルの何行目にあるか表示できます。

## 事の発端
djangoを使ってカスタムユーザモデルを作成しメールアドレスで認証したいけど、設定してないはずのusernameが認証必須になってる。

そんな設定したっけ？

そんな時に以下のコマンドを実行しました。
```find . -name "*" -type f |　xargs grep -n "required"```

## 結果
めっちゃヒットした。。。

```
./venv/lib/python3.12/site-packages/django/contrib/admin/static/admin/js/vendor/xregexp/xregexp.js:1355:  r2.lastIndex = pos; // Fixed `exec` required for `lastIndex` fix, named backreferences, etc.
./venv/lib/python3.12/site-packages/django/contrib/admin/static/admin/js/vendor/xregexp/xregexp.js:1695:  } // Fixed `replace` required for named backreferences, etc.
~省略~
./venv/lib/python3.12/site-packages/boto3/resources/model.py:574:                    data_required = True
./venv/lib/python3.12/site-packages/boto3/resources/model.py:577:            if subresources and not data_required:
./venv/lib/python3.12/site-packages/boto3/resources/model.py:579:            elif not subresources and data_required:
```

件数にすると1921件。
```
(venv) user@usernoMacBook-Pro % find . -name "*" -type f | xargs grep -n "required" | wc -l
    1921
```

これではどこの設定が悪さをしてるのか探し用がありません。

除外検索をしてみるか、と思ってネットで調べてみると参考になりそうな記事を見つけました。
[grepコマンドで特定のディレクトリを検索対象から除外する](https://tex2e.github.io/blog/shell/grep-exclude-dir)

実際に試してみると。。
```find . -name "*" -type f | xargs grep -r --exclude-dir venv "required" | grep -n "required"```

めっちゃヒット。。。（２回目）
件数にして1531件。
```
(venv) user@usernoMacBook-Pro % find . -name "*" -type f | xargs grep -r --exclude-dir venv "required" | grep -n "required" | wc -l
    1531
```

ならばとパイプを繋げて以下のコマンドで解決。
※`venv`ディレクトリと`./venv`ディレクトリを|（パイプ）で繋ぐことで検索から除外してます。
```
find . -name "*" -type f | xargs grep -r --exclude-dir venv "required" | grep -r --exclude-dir ./venv "required" | grep -n "required"
```

```
実行結果
1:./config/templates/login.html:      <input type="email" class="form-control" id="floatingInput" placeholder="name@example.com" name="email" required>
2:./config/templates/login.html:      <input type="password" class="form-control" id="floatingPassword" placeholder="Password" name="password" required>
3:./config/templates/signup.html:      <input type="text" class="form-control" id="floatingInput" placeholder="Username" name="username" required>
4:./config/templates/signup.html:      <input type="email" class="form-control" id="floatingInput" placeholder="xxxx@example.com" name="email" required>
5:./config/templates/signup.html:      <input type="text" class="form-control" id="floatingInput" placeholder="080-XXXX-XXXX" name="telephone_number" required>
6:./config/templates/signup.html:      <input type="text" class="form-control" id="floatingPassword" placeholder="YYYY-MM-DD" name="date_of_birth" required>
7:./config/templates/signup.html:      <input type="password" class="form-control" id="floatingPassword" placeholder="Password" name="password" required>
```
だいぶスッキリしましたね。

結局こちらでusernameを認証する設定は入れておらず、djangoのデフォルト設定でユーザ名の認証が有効になっており、デフォルトの認証設定をメールアドレスへ変更する必要があることがわかりました。

## まとめ
開発者側で身に覚えのない設定が原因でエラーが発生したときはすべての環境を俯瞰的に確認し、人の手による介入がないようであればデフォルトの設定を視野に入れると解決の糸口が見つかるかもしれません。