pipで導入したスクリプトがcommand not foundで動作しなかった

## 発端
Mac環境で`.msg`形式でファイルに保存されたメールを読みたかった。自身に添付ファイルつきメールを送付する方法では読めなかったため、[msg-extractor](https://github.com/TeamMsgExtractor/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が記録されていない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/57739/0301bce6-651e-71a9-58b7-9751725dbd23.png)

その時点で実行されていた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
```