LoginSignup
5
2

More than 1 year has passed since last update.

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

Last updated at Posted at 2018-10-31

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出来ない模様)

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

全部某サービスが悪い。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2