😕

Git for Windows の Git\bin\bash.exe と Git\usr\bin\bash.exe の違い

2022/02/13に公開

要旨

Git\bin\bash.exe で起動した Bash では PATH の先頭に /mingw64/bin が付与されるので find などの同名 EXE を誤実行する心配はない。スクリプトの実行にはこちらを使うべし。

対して Git\usr\bin\bash.exe で起動した Bash では同名 EXE を誤って実行しないように気をつける必要がある。特に Linux 等と共通使用しているシェルスクリプトでは要注意。
(同名ファイルの例:C:\Windows\System32\find.exe, C:\Program Files(x86)\Embarcadero\Studio\21.0\bin\grep.exe)

The difference between Git\bin\bash.exe and Git\usr\bin\bash.exe on Git for Windows

本記事は下記 Stack Overflow 記事を補足する情報として書きます。
https://stackoverflow.com/questions/53980686/in-the-install-path-of-git-for-windows-whats-the-difference-between-git-bash

Git\bin\bash.exe は何者か

それは Git wrapper です。公式文書にこれだけちゃんと書いてあるのに私は全く知りませんでした。検索でここに辿り着くのは至難の業、あぁ何とググラビリティの低いキーワードでしょう。
https://github.com/git-for-windows/git/wiki/Git-wrapper

千行に満たないコードですのでソースを直読みした方が速いかもしれません。
https://github.com/git-for-windows/MINGW-packages/blob/main/mingw-w64-git/git-wrapper.c

シェルスクリプトの単体実行で Git\usr\bin\bash.exe を使ってひどい目にあった

例えば下記のようなスクリプトを "C:\Program Files\Git\usr\bin\bash.exe" で実行するとエラーになる場合があります。

check_code_CRLF.sh
#!/bin/bash
# CRLF 以外の C/C++/C# ソースコードが無いかをチェックする
PATH=$PATH:/usr/bin
find ./src -regex '.*\.[ch][sp]*$' |xargs -d '\n' file |grep -v 'CRLF \|CRLF, NEL\|empty'
rc=$?
if [ $rc -eq 0 ]; then
  echo "=========================================="
  echo "ERROR. Found invalid (CR, LF or MIXED) line endings in C, C# source code. It should be CRLF line endings."
  exit 1;
fi
> "C:\Program Files\Git\usr\bin\bash.exe" -c "./check_code_CRLF.sh"
FIND: パラメーターの書式が違います
Usage: file [-bcCdEhikLlNnprsSvzZ0] [--apple] [--extension] [--mime-encoding]
            [--mime-type] [-e <testname>] [-F <separator>]  [-f <namefile>]
            [-m <magicfiles>] [-P <parameter=value>] [--exclude-quiet]
            <file> ...
       file -C [-m <magicfiles>]
       file [--help]

これは、スクリプト内の find を C:\Windows\System32\find.exe で実行してしまったからです。 Windows の find.exe は GNU find とも BSD find とも似ても似つかないコマンドですから。

find だけならば /usr/bin/find と指定すれば C:\Windows\System32\find.exe の誤起動は回避できます。

/usr/bin/find ./src -regex '.*\.[ch][sp]*$' |xargs -d '\n' file |grep -v 'CRLF \|CRLF, NEL\|empty'

しかし、これでもダメな場合がありました。
grep です。私の環境では grep を "C:\Program Files(x86)\Embarcadero\Studio\21.0\bin\grep.exe" で起動していました。 なんと Turbo GREP です。
これには気がつきませんでした。おかげでずいぶん悩みました。

> "C:\Program Files\Git\bin\bash.exe" -xc "echo aaa | grep 'aaa\|bbb'
+ echo aaa
+ grep 'aaa\|bbb'
aaa
> "C:\Program Files\Git\usr\bin\bash.exe" -xc "echo aaa | grep 'aaa\|bbb'
+ echo aaa
+ grep 'aaa\|bbb'

とてもシンプルな 'aaa\|bbb' という正規表現検索の結果が異なるのは本当に戸惑いました。
後者(Git\usr\bin\bash.exe)は Turbo GREP が動いていたので \| を OR として認識していなかったという落ちでした。(\| が GNU grep 拡張だというのも全く知りませんでした orz...)

長々と書いてしまいましたが、これらから得た教訓はシンプルそのもの、
シェルスクリプトを実行するときは 「Git\bin\bash.exe を使うべし!」
でした。

そもそもなぜ Windows でシェルスクリプトを動かしたかったのか

Jenkins 等の CI 環境で Linux と Windows の agent をそれぞれ動かしている場合、Linux と Windows の両方で bash のシェルスクリプトを実行したくなることがよくあります。

例えばソースコードの改行コードや漢字コードをビルド前にチェックするスクリプトなど、既にあるちょっとしたシェルスクリプトを Windows でも動かしたい。
そういうときに Git for Windows 付属の bash を重宝して使用しています。 CI の agent であれば Git for Windows はほぼ入っていますから。

(END OF DOCUMENT)

GitHubで編集を提案

Discussion