発端
Mac環境で.msg
形式でファイルに保存されたメールを読みたかった。自身に添付ファイルつきメールを送付する方法では読めなかったため、msg-extractorを導入した。
pipでインストールしてから実行したところ、へんじがない。
$ extract_msg
pyenv: extract_msg: command not found
結論
- pyenvから呼ぶエントリポイントスクリプトである
~/.pyenv/versions/anaconda3-5.3.1/bin/extract_msg
に実行権限がついていなかった - さらに
~/.pyenv/versions/anaconda3-5.3.1/bin/extract_msg
の改行コードがCRLFだった
これらを修正したところ正常に動作するようになった。ツールとしては大変満足で、エクセルやpngの添付ファイルまで分離してくれました。
問題判別の試行錯誤
原因の特定まで時間を溶かしたため、脳内ダンプを兼ねて調査の流れを記録に残す。
pipでインストールされた内容を確認する
$ pip show extract-msg
Name: extract-msg
Version: 0.27.9
Summary: Extracts emails and attachments saved in Microsoft Outlook's .msg files
Home-page: https://github.com/TeamMsgExtractor/msg-extractor
Author: The Elemental of Destruction & Matthew Walker
Author-email: arceusthe@gmail.com, mattgwwalker@gmail.com
License: GPL
Location: /Users/haruyosh/.pyenv/versions/anaconda3-5.3.1/lib/python3.7/site-packages
Requires: compressed-rtf, olefile, imapclient, tzlocal
Required-by:
-
/Users/haruyosh/.pyenv/versions/anaconda3-5.3.1/lib/python3.7/site-packages
にextract_msg/
とextract_msg-0.27.9.dist-info/
が存在する -
extract_msg/
には__main__.py
が存在する -
__main__.py
を生で呼び出すと正しく動作する
少なくとも生スクリプトは動作することが分かったので、呼び出し元から一つずつ皮を剥いていくことにする。
スクリプトを見ていく
どこにあるのか、内容はどうなっているのか。まず1段目に呼び出しているのは次のスクリプト。
$ which extract_msg
/Users/haruyosh/.pyenv/shims/extract_msg
後で気づいたことだが、~/.pyenv/shims
にあるスクリプトはラッパースクリプトの扱いで、全て同一内容(っぽい)。
$ ls -l ~/.pyenv/shims/ | head -6
total 2896
-rwxr-xr-x 1 haruyosh staff 418 9 16 13:53 2to3
-rwxr-xr-x 1 haruyosh staff 418 9 16 13:53 2to3-3.7
-rwxr-xr-x 1 haruyosh staff 418 9 16 13:53 Assistant.app
-rwxr-xr-x 1 haruyosh staff 418 9 16 13:53 Designer.app
-rwxr-xr-x 1 haruyosh staff 418 9 16 13:53 Linguist.app
正常動作しているipythonと比べておく。同一内容。
$ diff ~/.pyenv/shims/{extract_msg,ipython}
$
この同一内容であるスクリプトの中身を見ると、ファイル末尾に実行内容が書いてあった。
$ tail -4 `which extract_msg`
fi
export PYENV_ROOT="/Users/haruyosh/.pyenv"
exec "/usr/local/Cellar/pyenv/1.2.20/libexec/pyenv" exec "$program" "$@"
さらに実行先ファイルの中を見てみると、デバッグオプションがあることが分かった。
$ head -8 /usr/local/Cellar/pyenv/1.2.20/libexec/pyenv
#!/usr/bin/env bash
set -e
if [ "$1" = "--debug" ]; then
export PYENV_DEBUG=1
shift
fi
デバッグオプションをつけて実行し、正常動作しているipythonと今回動作させたいextract_msgの出力を突合する方針にした。
$ PYENV_ROOT="/Users/haruyosh/.pyenv" /usr/local/Cellar/pyenv/1.2.20/libexec/pyenv --debug exec extract_msg 2> extract_msg.stderr
$ PYENV_ROOT="/Users/haruyosh/.pyenv" /usr/local/Cellar/pyenv/1.2.20/libexec/pyenv --debug exec ipython 2> ipython.stderr
比較して見ていくと、145行目まで同一内容だった。しかし146行目、ipythonはbreakして抜けているところ、extract_msgにはbreakが記録されていない。
その時点で実行されていたpyenv-whichの該当箇所を見てみる。
$ less -N /usr/local/Cellar/pyenv/1.2.20/libexec/pyenv-which
:
49 if [ -x "$PYENV_COMMAND_PATH" ]; then
50 break
51 fi
:
ここでは渡したスクリプトの実行権限を見ている。extract_msgには実行権限がなくてbreakしなかったということになる。実際に見てみると確かにextract_msgに実行権限がない!
$ ls -l ~/.pyenv/versions/anaconda3-5.3.1/bin/{extract_msg,ipython}
-rw-r--r-- 1 haruyosh staff 74 12 24 17:12 /Users/haruyosh/.pyenv/versions/anaconda3-5.3.1/bin/extract_msg
-rwxr-xr-x 1 haruyosh staff 270 5 17 2019 /Users/haruyosh/.pyenv/versions/anaconda3-5.3.1/bin/ipython
ここまででビンゴだと思ったがぬか喜びだった。実行してみるがさらにダメ。
$ chmod +x ~/.pyenv/versions/anaconda3-5.3.1/bin/extract_msg
$ extract_msg
env: python\r: No such file or directory
'\r'
ってなんなのよもう。でも進研ゼミでやったやつだ
$ od -c ~/.pyenv/versions/anaconda3-5.3.1/bin/ipython | head -4
0000000 # ! / U s e r s / h a r u y o s
0000020 h / . p y e n v / v e r s i o n
0000040 s / a n a c o n d a 3 - 5 . 3 .
0000060 1 / b i n / p y t h o n \n \n #
$ od -c ~/.pyenv/versions/anaconda3-5.3.1/bin/extract_msg | head -4
0000000 # ! / u s r / b i n / e n v p
0000020 y t h o n \r \n \r \n f r o m e x
0000040 t r a c t _ m s g . _ _ m a i n
0000060 _ _ i m p o r t m a i n \r \n
こうして、こうじゃ
$ tr -d '\r' < extract_msg > extract_msg.new
$ mv extract_msg extract_msg.old
$ mv extract_msg.new extract_msg
動くかな?
$ extract_msg
pyenv: extract_msg: command not found
おっと。そうだね
$ ls -l extract_msg*
-rw-r--r-- 1 haruyosh staff 69 12 24 18:04 extract_msg
-rwxr-xr-x 1 haruyosh staff 74 12 24 17:12 extract_msg.old
これでどうじゃ
$ chmod +x extract_msg
動いた!
$ extract_msg
usage: extract_msg [-h] [--use-content-id] [--dev] [--validate] [--json]
[--file-logging] [--verbose] [--log LOG]
[--config CONFIG_PATH] [--out OUT_PATH] [--use-filename]
[--dump-stdout]
msg [msg ...]
extract_msg: error: the following arguments are required: msg