シェルスクリプトマガジン

第11回 大切なメールだけをチャットルームに送る

実際のプログラム例

 では、シェルスクリプトを作成してみましょう。今回のシェルスクリプト(important_message.sh)は、次のようになります。「Googleアカウントのユーザー名」と「Googleアカウントのパスワード」は重要なメッセージが届くGmailのユーザー名とパスワードに、「トークン」は、Slack Appを作成したときに生成された認証アクセス用トークンに書き換えてください。

※実際のスクリプトから一部の文字($など)が抜け落ちています(原因は調査中)。正しいスクリプトを「 https://github.com/shellscript-magazine/rensai_shellscript1 」から入手してください。
※受信数を取得する部分が不安定だったので、31行目のコードを修正しました(2019年11月9日)。

 それでは、シェルスクリプトを1行ずつ見ていきましょう。1行目は、第1回で説明した「シェバン」です。3行目の「#」で始まる行はコメントです。8、11、15、33行目、51行目、54行目のコメント行になります。

 4~6行目は、Gmailにアクセスするための「受信メールサーバーのホスト名」「Googleアカウントのユーザー名」「Googleアカウントのパスワード」を、それぞれ「pop_server」「user_id」「password」の変数に代入しています。

 9行目は「【重要】」というBase64でエンコードした文字列の一部を変数「subject_base64」に代入しています。日本語で書かれた、メールのタイトルは、

のように受信されます。この文字列は、受信したメールのタイトルの先頭が「【重要】」であるかどうかのチェック(55行目)に利用しています。

 12行目は、Slackのチャンネル名(ここでは「シェルスクリプト連載」)をURLエンコードして変数「channel_urlencoding」に代入しています。URLエンコードの部分は、「Slack Appを生成する」の章で実行した最初のコマンドと同じです。URLエンコードに適したコマンドがないので、Pythonの「urllib.parse」ライブラリを用いてエンコードしています。チャンネル名は、関数「sys.stdin.read()」を使ってパイプライン(|)経由で標準入力から取り込んでいます。標準入力から受け取ったチャンネル名には改行コード(LF)が含まれているので「rstrip()」関数で末尾の改行を削除しています。URLエンコードする関数は「urllib.parse.quote()」です。

 13行目は、認証アクセス用のトークンを変数「token」に代入しています。この変数は、58行目でSlackにメッセージを転送するために利用します。

 16~31行目では、受信メールサーバーにアクセスして受信メッセージの件数を取得しています。第7回と同様に、expectコマンドを利用しています。18行目の「spawn」の後ろにあるコマンドで、受信メールサーバーへアクセスしています。「expect」が期待する応答、「send」が入力する文字列です。期待する応答が返ってこないときのタイムアウトは、17行目の「set timeout」で設定しています。ここでは「30」秒という長いタイムアウトにしています。タイムアウトを長くしないと、29行目のリダイレクト(>)で書き込んだログが「receive.log」ファイルに正しく残りませんでした。

 24行目の「send \”stat\n\”」で受信メッセージ数を取得しています。応答は、次のように返ってきます。

 この応答はログファイルの下部に書き込まれています。31行目の「grep +OK receive.log | tail -n 2 | head -n 1」で、その行を切り出します。「grep」コマンドで「+OK」で始まる応答部分を抽出して「tail」コマンドで末尾2行だけ切り出し、「head」コマンドでその中から先頭の1行を取り出しています。その後ろの「cut -d ” ” -f 2」で、2列目にある受信メッセージ数を抽出して変数「receive_count」に代入しています。

 34~59行目で「for」文の処理を受信メッセージ数分実行します。for文に関しては、第1回を参照してください。

 36~49行目は、受信メッセージを1通ずつ読み出す処理です。44行目の「retr 1」が読み出すコマンドで、受信メッセージの最後が「.」で終わるので、45行目で応答を確認しています。一度受信したメッセージはサーバーから削除されます。

 49行目で受信メッセージを含んだログを「message.log」ファイルに取り込んでいます。52行目でBase64でエンコードされたタイトルの先頭から16文字分を取り込んで変数「subject」に代入しています。

 55行目でこの16文字からタイトルの先頭が「【重要】」であるかどうかを判断して、「【重要】」であれば「if」文内の処理を実行します。if文に関しては、第1回を参照してください。

 56行目でログからメッセージ部分を切り出しています。プログラミング言語「AWK」を使うと、次のように開始部分と終了部分を指定するだけでメッセージを切り出せます。

 ここでの切り出しには、受信メッセージヘッダーの最後にある「Content-Transfer-Encoding: 8bit」を開始部分として、受信メッセージの終わりにある「.」を終了部分として利用しています。受信メッセージヘッダーの最後は、メールを送信するクライアント(エージェント)ソフトによって異なります。実際に、message.logファイルを確認して自分の環境に合わせて変更してください。

 なお、「:」「.」は特殊文字なのでエスケープ(手前に「\」を付ける)しています。また、行頭を表す正規表現の「^」も利用しています。

 開始部分と終了部分が切り出した受信メッセージに含まれているので「head -n -1 | tail -n +2」で取り除いて「message.txt」ファイルに格納しています。

 57行目は受信メッセージをURLエンコードする処理です。Slackに転送するメッセージをファイルから読み出すために「cat」コマンドを使っていますが、「Slack Appを生成する」の章で実行した、「echo」コマンドを使っている2番目のコマンドと同じです。URLエンコードの処理は、前述したPythonの「urllib.parse」ライブラリを用いたものと同様です。

 Slackに転送するURLエンコーディングした受信メッセージが用意できたので、58行目の「curl」コマンドでSlackに対してメッセージを送信しています。これは「Slack Appを生成する」の章で実行した3番目のコマンドです。