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 '/'
起きてる事象はopensslをMSYS上で使った場合に引数がうまく動いていない、という事象だったが、根本的な部分は一緒。
回答コメントに「MSYSを考慮していないプログラムだと、Posix PATHからWindows PATHへの変換が起きるよ」みたいなコメントがあり、事象と相まって納得感があった。(でもその変換規則はわからんわ)
投稿にあった変換についてみてみると、msys-X.0.dllに依存していない時に行います、みたいなことが書いてあった。
straceコマンドがついてきていたので試してみると、確かに、msys-X.0.dllをロードしているとき、していない時で動作が違うようだった。(またstraceもmsys-X.0.dllを使ったプログラムでないとロクにtrace出来ない模様)
とりあえず原因が分かったので、すっきり。
全部某サービスが悪い。