MSYS上では引数が勝手に変換される場合がある

tl;dr
------

MSYSの考慮が無いプログラムに対して引数を与えたとき、`/`について意図しない変換が起きる場合があります。
（考慮が無いプログラム=msys-x.0.dllが使われていないプログラム。WindowsインストーラでいれたPythonなど）

上記に該当するプログラムについて、Posix PathからWindows上のPathへの変換を行われることを考慮する必要があります。

変換される条件は以下を参照。

~~http://www.mingw.org/wiki/Posix_path_conversion~~

mingw.orgのドメインが期限切れしているので代用
https://www.msys2.org/docs/filesystem-paths/#automatic-unix-windows-path-conversion

### 2022/02/09 追記 もう少し具体的な

見てもらえているようなので、もう少しわかってきた範囲を補足。

具体的な事象としては、 `/` を含む引数を与えると変換が発生する場合があります。

以下のように、MSYSなBash（Git for windowsに付属してくるGit Bash等）で、通常のWindowsインストーラでインストールしたpython（MSYYSの考慮が無いプログラム）を動かすと発生します。

```
$ python -c "import os; print(os.sys.argv)" 'foo=/bar/baz'
['-c', 'foo=C:/Program Files/Git/bar/baz']
```


この変換に対して上記の引数の通り渡したい場合、最初の`/`は`//`にし、以降の`/`を`\`(バックスラッシュ)にします。

```
$  python -c "import os; print(os.sys.argv)" 'foo=//bar\baz'
['-c', 'foo=/bar/baz']
```

単純にこの変換を止めてほしい場合は環境変数 `MSYS2_ARG_CONV_EXCL`に`*`を与えて除外するのがよさそうです。
（前方一致でセミコロン区切りで指定もできますが面倒なので`*`にしたいときもあるでしょう）

```
$ MSYS2_ARG_CONV_EXCL="foo" python -c "import os; print(os.sys.argv)" 'foo=/bar/baz' 'fuu=/bar/baz'
['-c', 'foo=/bar/baz', 'fuu=C:/Program Files/Git/bar/baz']

$ MSYS2_ARG_CONV_EXCL="f" python -c "import os; print(os.sys.argv)" 'foo=/bar/baz' 'fuu=/bar/baz'
['-c', 'foo=/bar/baz', 'fuu=/bar/baz']

MSYS2_ARG_CONV_EXCL="fo;fu" python -c "import os; print(os.sys.argv)" 'foo=/bar/baz' 'fuu=/bar/baz'
['-c', 'foo=/bar/baz', 'fuu=/bar/baz']

$ MSYS2_ARG_CONV_EXCL="*" python -c "import os; print(os.sys.argv)" 'foo=/bar/baz' 'fuu=/bar/baz'
['-c', 'foo=/bar/baz', 'fuu=/bar/baz']
```

なお、環境変数も同様の動きをします。こちらは環境変数 `MSYS2_ENV_CONV_EXCL`という別の環境変数で同様に対応できます。

```
$ FOO=/bar/baz python -c "import os; print(os.environ.get('FOO'))"
C:/Program Files/Git/bar/baz

$ FOO=/bar/baz MSYS2_ENV_CONV_EXCL="FOO" python -c "import os; print(os.environ.get('FOO'))"
/bar/baz
```

確認環境
--------

- Windows 10 Pro
- Git for Windowsに付属してきたBash上にて確認(gitのバージョンはgit version 2.18.0.windows.1)


背景
-----

ある日、某サービスが某Storageが勝手に用意してくれるファイルが`resourceId=/SUBSCRIPTIONS/...`という形式になっており、これをprefixで絞り込んでダウンロードするツールを書こうと思った。

Git for windows付属のbashを開き、サクッとpythonで引数を取るプログラムを書いたときにおかしな解釈をされた。

要約のコードは以下。

```
python -c "import os; print(os.sys.argv)" 'foo=/bar'
```

> ['-c', 'foo=**C:/Program Files/Git**/bar']

この`C:/Program Files/Git`はどこから出てきたのか。引数には無い。


なお、Windowsのcmd上でも起きないし、もちろんLinux(ubuntu)のbash上では起きない。


他のプログラムで起きないか調べる
---------------

```
perl -e "print @ARGV" -- "foo=/bar"
```

> foo=/bar

```
bash -c 'echo $*' -- "foo=/bar"
```

> foo=/bar

そりゃそうですよね。

`C:/Program Files/Git` は多分、`git-bash.exe`がある場所で、多分ここがルートディレクトリ的な扱いなんだと思われる。Windows上でUNIXっぽい構造を再現しようとしているから変になっているのか？
でも今までこんな問題に引っかかったことなんてなかったし、変な事やったんじゃ・・・

という感じで、原因がますますわからなくて、bashがやっているわけでもなさそう、pythonがやっているのか？とか思い始めた。

ふと、pythonは`C:\Python36`にあるMSYSの外にあるプログラムだという事を思い出した。PATHが勝手に通るので気にしていなかった。

もしかして、同じ状況下のGolang(`C:\go`)はどうだろう、と思って以下のようにした。


```
package main

import (
    "fmt"
    "os"
)

func main() {
    fmt.Printf("args: %s\n", os.Args)
}
```

```
C:/go/bin/go run test.go foo=/bar
```

> args: [C:\...\exe\test.exe foo=**C:/Program Files/Git**/bar]

なるほど。もしかして`C:/`から指定したらいけるのか？

```
# たまたまあったbash(多分使われていない)
"C:/Program files/Git/bin/bash" -c 'echo $*' -- "foo=/bar"
```

> foo=C:/Program Files/Git/bar

お！

```
# 環境変数 $SHELL に設定されているbash
"C:/Program files/Git/usr/bin/bash" -c 'echo $*' -- "foo=/bar"
```

> foo=/bar

・・・わからない。

また、`diff -a "/usr/bin/bash" "/bin/bash"`は差分が無い、といわれるが、
`diff "C:/Program files/Git/bin/bash" /bin/bash`としたとき、差分があるといわれる。逆ならわかるんだけど...

MSYSの気持ちがわからない。


なお、`//`にすると回避できます。なんだそれ・・・

```
C:/go/bin/go run test.go foo=//bar
```
> args: [C:\...\exe\test.exe foo=/bar]


### 追記, `/` が複数出る場合で挙動が違う

```
$ python -c "import os; print(os.sys.argv)" 'foo=/bar'
['-c', 'foo=C:/Program Files/Git/bar']

$ python -c "import os; print(os.sys.argv)" 'foo=//bar'
['-c', 'foo=/bar']
```

これだけみると、「なんだ、スラッシュを2つ並べて`//`にすればいいのね？」と思うかもしれませんが、2つ以上区切っていると反する挙動をします。

```
$ python -c "import os; print(os.sys.argv)" 'foo=/bar/baz'
['-c', 'foo=C:/Program Files/Git/bar/baz']

$ python -c "import os; print(os.sys.argv)" 'foo=//bar/baz'
['-c', 'foo=//bar/baz']

$ python -c "import os; print(os.sys.argv)" 'foo=//bar//baz'
['-c', 'foo=//bar//baz']
```

正解は最初の`/`は`//`にし、以降の`/`を`\`(バックスラッシュ)にする、です。

```
$  python -c "import os; print(os.sys.argv)" 'foo=//bar\baz'
['-c', 'foo=/bar/baz']
```

これでは辛いので、`MSYS2_ARG_CONV_EXCL`環境変数でキーワードを含めることで、引数毎に前方一致するものについて、この変換の挙動を止めることができます。セミコロン区切りで複数かけます。`*`でワイルドカードになります

```
$ MSYS2_ARG_CONV_EXCL="foo" python -c "import os; print(os.sys.argv)" 'foo=/bar/baz' 'fuu=/bar/baz'
['-c', 'foo=/bar/baz', 'fuu=C:/Program Files/Git/bar/baz']

$ MSYS2_ARG_CONV_EXCL="f" python -c "import os; print(os.sys.argv)" 'foo=/bar/baz' 'fuu=/bar/baz'
['-c', 'foo=/bar/baz', 'fuu=/bar/baz']

MSYS2_ARG_CONV_EXCL="fo;fu" python -c "import os; print(os.sys.argv)" 'foo=/bar/baz' 'fuu=/bar/baz'
['-c', 'foo=/bar/baz', 'fuu=/bar/baz']

$ MSYS2_ARG_CONV_EXCL="*" python -c "import os; print(os.sys.argv)" 'foo=/bar/baz' 'fuu=/bar/baz'
['-c', 'foo=/bar/baz', 'fuu=/bar/baz']
```





色々調べた
------------

これをどういう風に調べるか悩んだが、引数のスラッシュがmsys上のbashで変な動きするから、stackoverflowで`msys argument slash`と調べたら、以下の投稿がヒットした。

[Running Openssl from a bash script on windows - Subject does not start with '/'](https://stackoverflow.com/questions/31506158/running-openssl-from-a-bash-script-on-windows-subject-does-not-start-with/31990313#31990313)

起きてる事象はopensslをMSYS上で使った場合に引数がうまく動いていない、という事象だったが、根本的な部分は一緒。
回答コメントに「MSYSを考慮していないプログラムだと、[Posix PATHからWindows PATHへの変換](https://www.msys2.org/docs/filesystem-paths/#automatic-unix-windows-path-conversion)が起きるよ」みたいなコメントがあり、事象と相まって納得感があった。（~~でもその変換規則はわからんわ~~）

投稿にあった変換についてみてみると、msys-X.0.dllに依存していない時に行います、みたいなことが書いてあった。
straceコマンドがついてきていたので試してみると、確かに、msys-X.0.dllをロードしているとき、していない時で動作が違うようだった。（またstraceもmsys-X.0.dllを使ったプログラムでないとロクにtrace出来ない模様）

とりあえず原因が分かったので、すっきり。

全部某サービスが悪い。