Add 2026-08-20_Windows Git Bash 上使用 ADB 的問題
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
---
|
||||
slug: Windows Git Bash 上使用 ADB 的問題
|
||||
title: Windows Git Bash 上使用 ADB 的問題
|
||||
description:
|
||||
toc: true
|
||||
authors:
|
||||
- awin
|
||||
tags:
|
||||
- windows
|
||||
- terminal
|
||||
categories:
|
||||
- System
|
||||
series:
|
||||
- Windows
|
||||
date: 2026-08-20T00:00:00
|
||||
lastmod: 2026-08-20T00:00:00
|
||||
featuredVideo:
|
||||
featuredImage:
|
||||
draft: false
|
||||
enableComment: true
|
||||
---
|
||||
|
||||
# Windows Git Bash 上使用 ADB 的路徑轉換問題
|
||||
|
||||
在 Windows 的 Git Bash 裡使用 ADB 時,可能會遇到一個讓人摸不著頭緒的問題:明明輸入的是正確 Android path,ADB 收到的卻像是 Windows path。
|
||||
|
||||
<!--more-->
|
||||
|
||||
例如,要把目前目錄的 `1.txt` 傳到 Android 裝置:
|
||||
|
||||
```zsh
|
||||
adb push 1.txt /data/local/tmp/
|
||||
```
|
||||
|
||||
這條命令裡其實同時存在兩個檔案系統:
|
||||
|
||||
```text
|
||||
1.txt Windows 本機檔案
|
||||
/data/local/tmp/ Android 裝置內的目的地
|
||||
```
|
||||
|
||||
對 ADB 來說,兩者的意義很清楚;但 Git Bash 背後的 MSYS2 runtime 只看得到命令列字串。它看到 `/data/local/tmp/` 這種以 `/` 開頭的參數時,可能會以為這是本機 POSIX path,於是在啟動 Windows 原生的 `adb.exe` 前,先把它轉成 Windows path。
|
||||
|
||||
結果,一個原本屬於 Android 的 path,就這樣被「好心辦壞事」地改寫了。
|
||||
|
||||
## Git Bash 為什麼要自動轉換 path?
|
||||
|
||||
Git Bash 提供類 Unix 的操作環境,使用的 path 長得像這樣:
|
||||
|
||||
```text
|
||||
/c/Users/<username>/Downloads/1.txt
|
||||
```
|
||||
|
||||
Windows 原生程式通常期待的是:
|
||||
|
||||
```text
|
||||
C:\Users\<username>\Downloads\1.txt
|
||||
```
|
||||
|
||||
為了讓 Git Bash 可以順利呼叫 Windows 程式,Git for Windows 所附的 MSYS2 runtime 會檢查傳給原生 `.exe` 的參數,並嘗試將 POSIX path 自動轉成 Windows path。
|
||||
|
||||
```text
|
||||
Git Bash Windows 原生程式
|
||||
|
||||
/c/Users/me/file.txt
|
||||
|
|
||||
| MSYS2 path conversion
|
||||
v
|
||||
C:\Users\me\file.txt -------> adb.exe、subl.exe、其他 CLI
|
||||
```
|
||||
|
||||
對大部分 Windows CLI 而言,這項功能非常實用。問題是,MSYS2 無法知道某個 `/` 開頭的參數究竟代表:
|
||||
|
||||
- Windows 本機檔案的 POSIX 表示法;或
|
||||
- Android、Docker、遠端主機等另一個系統內的 path。
|
||||
|
||||
當兩者長得一樣,自動判斷就可能出錯。
|
||||
|
||||
## `MSYS_NO_PATHCONV` 是什麼?
|
||||
|
||||
Git for Windows 提供 `MSYS_NO_PATHCONV`,用來停用傳給 Windows 原生程式的 POSIX-to-Windows path conversion。
|
||||
|
||||
```zsh
|
||||
MSYS_NO_PATHCONV=1 adb push 1.txt /data/local/tmp/
|
||||
```
|
||||
|
||||
設定後,`/data/local/tmp/` 會原樣傳給 `adb.exe`,不再被改寫成本機 Windows path。
|
||||
|
||||
它的行為可以整理成:
|
||||
|
||||
```text
|
||||
沒有設定 MSYS_NO_PATHCONV
|
||||
-> 使用預設的自動 path conversion
|
||||
|
||||
設定 MSYS_NO_PATHCONV=1
|
||||
-> 停用自動 path conversion
|
||||
```
|
||||
|
||||
需要特別注意:`MSYS_NO_PATHCONV=1` 不是開啟轉換,而是關閉轉換。若要恢復預設行為,應該移除這個環境變數:
|
||||
|
||||
```zsh
|
||||
unset MSYS_NO_PATHCONV
|
||||
```
|
||||
|
||||
不要把 `MSYS_NO_PATHCONV=0` 當成「重新開啟轉換」;這是一個是否存在的停用開關,不是一般的 `0`/`1` Boolean 設定。
|
||||
|
||||
## 不要全域停用 path conversion
|
||||
|
||||
最直接的做法看起來是把下面這行寫進 shell 設定檔:
|
||||
|
||||
```zsh
|
||||
export MSYS_NO_PATHCONV=1
|
||||
```
|
||||
|
||||
但這會影響之後啟動的所有 Windows 原生程式。
|
||||
|
||||
例如,原本可以在 Git Bash 使用 POSIX path 呼叫 Sublime Text:
|
||||
|
||||
```zsh
|
||||
subl.exe /c/Users/<username>/notes.txt
|
||||
```
|
||||
|
||||
預設情況下,MSYS2 會將參數轉成正確的 Windows path。若全域停用轉換,`subl.exe` 會直接收到 `/c/Users/...`;Windows 原生程式可能將它視為目前磁碟機根目錄下的 `c\Users\...`,最後開啟錯誤的位置。
|
||||
|
||||
因此,真正需要的不是「整個 Git Bash 都不要轉換」,而是「只有呼叫 ADB 時不要轉換」。
|
||||
|
||||
## 正確做法:只替 ADB 停用轉換
|
||||
|
||||
可以為 ADB 建立一個 shell wrapper:
|
||||
|
||||
```zsh
|
||||
adb() {
|
||||
MSYS_NO_PATHCONV=1 command adb.exe "$@"
|
||||
}
|
||||
```
|
||||
|
||||
如果使用 Zsh,將它加入 `~/.zshrc`,再重新載入:
|
||||
|
||||
```zsh
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
如果使用 Bash,則加入 `~/.bashrc`:
|
||||
|
||||
```bash
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
這個 wrapper 做了三件事:
|
||||
|
||||
1. 讓輸入的 `adb ...` 先進入 shell function。
|
||||
2. 只替這次執行設定 `MSYS_NO_PATHCONV=1`。
|
||||
3. 用 `command adb.exe "$@"` 呼叫真正的 ADB,並完整保留所有參數。
|
||||
|
||||
如此一來,影響範圍只限於 ADB:
|
||||
|
||||
```text
|
||||
一般 Windows CLI
|
||||
-> 維持預設 path conversion
|
||||
|
||||
adb.exe
|
||||
-> 只在這次呼叫停用 conversion
|
||||
-> Android path 保持原樣
|
||||
```
|
||||
|
||||
## 停用轉換後,本機絕對路徑怎麼辦?
|
||||
|
||||
停用 path conversion 也會帶來另一個問題。以下命令的第一個 path 是 Windows 本機檔案:
|
||||
|
||||
```zsh
|
||||
adb push /c/Users/<username>/Downloads/1.txt /data/local/tmp/
|
||||
```
|
||||
|
||||
wrapper 會讓所有參數都保持原樣,因此 `adb.exe` 收到的本機來源仍是:
|
||||
|
||||
```text
|
||||
/c/Users/<username>/Downloads/1.txt
|
||||
```
|
||||
|
||||
這不是 Windows 原生 path。此時應使用 `cygpath`,主動將本機來源轉換成 Windows 格式:
|
||||
|
||||
```zsh
|
||||
adb push "$(cygpath -w /c/Users/<username>/Downloads/1.txt)" /data/local/tmp/
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
```text
|
||||
cygpath 在 POSIX 與 Windows path 格式之間轉換
|
||||
-w 輸出 Windows 格式
|
||||
$(...) 執行括號內的命令,並用輸出取代這一段
|
||||
"..." 確保含有空白的 path 仍被視為單一參數
|
||||
```
|
||||
|
||||
shell 展開後,實際傳給 ADB 的命令概念上會變成:
|
||||
|
||||
```zsh
|
||||
adb.exe push "C:\Users\<username>\Downloads\1.txt" /data/local/tmp/
|
||||
```
|
||||
|
||||
如果來源是目前目錄下的相對路徑,就不需要額外處理:
|
||||
|
||||
```zsh
|
||||
adb push 1.txt /data/local/tmp/
|
||||
```
|
||||
|
||||
相對路徑 `1.txt` 對 Git Bash 與 Windows 原生程式而言,都會以目前工作目錄為基準,因此通常可以直接使用。
|
||||
|
||||
## 判斷原則
|
||||
|
||||
遇到 path conversion 問題時,可以先問兩個問題:
|
||||
|
||||
```text
|
||||
這個 path 屬於哪一個檔案系統?
|
||||
|
|
||||
+-- Windows 本機
|
||||
| |
|
||||
| +-- POSIX 絕對路徑:用 cygpath -w 主動轉換
|
||||
| +-- 相對路徑:通常可直接使用
|
||||
|
|
||||
+-- Android/遠端系統
|
||||
|
|
||||
+-- 必須保持原樣,不可被 MSYS2 當成本機 path 轉換
|
||||
```
|
||||
|
||||
`MSYS_NO_PATHCONV` 並不是越常設定越好。它適合用在「目標程式有自己的 slash path 語意」的單次命令,例如 ADB;不適合成為整個 shell 的全域預設。
|
||||
|
||||
## 結論
|
||||
|
||||
這個問題的根源不在 ADB,也不在 Android,而是同一條命令同時跨越了三個世界:
|
||||
|
||||
```text
|
||||
Git Bash 的 POSIX path
|
||||
|
|
||||
v
|
||||
Windows 原生 adb.exe
|
||||
|
|
||||
v
|
||||
Android 裝置內的 path
|
||||
```
|
||||
|
||||
MSYS2 的自動轉換原本是為了打通 Git Bash 與 Windows,卻無法辨認 Android path。正確解法是縮小例外範圍:保留全域預設轉換,只在呼叫 ADB 時設定 `MSYS_NO_PATHCONV=1`;若本機來源使用 POSIX 絕對路徑,再以 `cygpath -w` 明確轉成 Windows path。
|
||||
|
||||
簡單來說:**本機 path 主動轉換,Android path 保持原樣,而且例外只套用在真正需要的命令上。**
|
||||
|
||||
## 參考資料
|
||||
- [MSYS2 Filesystem Paths](https://www.msys2.org/docs/filesystem-paths/)
|
||||
- [Git for Windows release notes:`MSYS_NO_PATHCONV`](https://github.com/git-for-windows/build-extra/blob/master/ReleaseNotes.md)
|
||||
Reference in New Issue
Block a user