在 Windows 上組合兩個變數會破壞它們
我正在嘗試在 Linux 的 Windows 系統上為 Bash 編寫一些東西,當通過
winpath
函式傳遞時,它會將 ~ 轉換為您的 Windows 使用者目錄。到目前為止,我能夠檢索 Windows 目錄,並將其轉換為 Unix 路徑,並且我還能夠找到/home/[username]/
通過~
. 我遇到麻煩的地方是將這兩者連接起來。我有兩個變數,如下所示:
$target_path=/home/jacob/Repositories $user_path=/mnt/c/Users/Jacob
(實際上我正在以程式方式檢索它們,但我認為這沒有什麼不同。)
/home/jacob
然後我從中刪除$target_path
,讓它保持原樣/Repositories
。目標是
$user_path
與 modified結合$target_path
,因此輸出為:/mnt/c/Users/Jacob/Repositories
為此,我只是在做:
target_path=$user_path$target_path
但是由於某種原因,正在發生的事情是輸出為:
/RepositoriesJacob
這沒有任何意義,因為當我
$user_path
自己輸出時,它是正確的,當我$target_path
自己輸出時,它也是正確的。因此,將這兩者結合起來會造成混亂。本要點中的相關行是 8-20 (完整程式碼粘貼在下面)。
winpath() { # get the Windows user path user_path=$(/mnt/c/Windows/System32/cmd.exe /C echo %HOMEDRIVE%%HOMEPATH%) # expand the specified path target_path=$(readlink -f $1) # change ~ to $user_path (WIP) if grep -q "^/home/" <<< $target_path; then # convert Windows-style user path to Unix-style (i.e. from C:\Users\[username] to /mnt/c/Users/[username]) temp_user_path=$(echo "$user_path" | sed -e 's|\\|/|g' -e 's|^\([A-Za-z]\)\:/\(.*\)|/mnt/\L\1\E/\2|') # remove /home/[username]/ from $target_path target_path=$(echo "$target_path" | sed -e 's|^/home/\(.*\)/\(.*\)|/\2|') # output $temp_user_path for debugging echo $temp_user_path # correctly outputs # output $target_path for debugging echo $target_path # correctly outputs # combine the variables echo $temp_user_path$target_path # DOES NOT correctly output (?) fi # check if a Windows path is getting parsed if grep -q "^/mnt/[a-z]/" <<< $target_path; then # swap /mnt/[a-z]/ with [A-Z]:/ and / with \ echo $(echo "$target_path" | sed -e 's|^\(/mnt/\([a-z]\)/\)\(.*\)|\U\2:\\\E\3|' -e 's|/|\\|g') else # return the user's home directory if a Unix path was parsed echo $user_path fi }
編輯:好的,這很奇怪……在Mac上嘗試這個,它工作正常。也許這是 WSL 的一些錯誤?
編輯2:經過進一步測試,看起來這與結合兩個
sed
輸出有關。如果我將字元串作為變數輸入並嘗試組合它們,它就可以正常工作。唔。
感謝@steeldriver 的幫助,我能夠解決這個問題!奇怪的是,這是 Windows 行尾的問題,即使 CMD 只輸出了一行。解決方案是將 CMD 輸出轉換為 Unix 樣式,這樣就解決了問題!這是我的最終程式碼:
winpath() { # get the Windows user path, convert to Unix line endings user_path=$(echo "$(/mnt/c/Windows/System32/cmd.exe /C echo %HOMEDRIVE%%HOMEPATH%)" | tr -d "\r") # expand the specified path target_path=$(readlink -f $1) # change ~ to $user_path if grep -q "^/home/" <<< $target_path; then temp_user_path=$(echo "$user_path" | sed -e 's|\\|/|g' -e 's|^\([A-Za-z]\)\:/\(.*\)|/mnt/\L\1\E/\2|' -e 's|^M$||') # if there was something after ~, add it to the end of the $user_path if grep -q "^/home/\(.*\)/\(.*\)" <<< $target_path; then target_path=$temp_user_path$(echo "$target_path" | sed -e 's|^/home/*/\(.*\)|/\2|') # if there was nothing after ~, $target_path is $user_path else target_path=$temp_user_path fi fi # check if a Windows path is getting parsed if grep -q "^/mnt/[a-z]" <<< $target_path; then # swap /mnt/[a-z] with [A-Z]: and / with \ echo $(echo "$target_path" | sed -e 's|^\(/mnt/\([a-z]\)\)\(.*\)|\U\2:\E\3|' -e 's|/|\\|g') else # return the user's home directory if a Unix path was parsed echo $user_path fi }
是的,額外的輸入是你的問題。
程序替換從程序的輸出中刪除最後的換行符(換行符),但在
cmd.exe
輸出 CR-LF 對的情況下,輸入將停留在user_path
. 列印時,CR 使輸出返回到列印時的行首。Repositories
長度相同,/mnt/c/Users
因此下面的斜線對齊在一個有點合乎邏輯的位置(而不是看起來像任何單詞被破壞了)。您可以使用
${user_path%$'\r'}
. (${var%pattern}
從變數中刪除與模式匹配的後綴)另外,我認為你的第二個 sed (
's|^/home/\(.*\)/\(.*\)|/\2|'
) 有點太熱心了,第一個.*
匹配最長的字元串,所以/home/user/foo/bar
會變成 just/bar
而不是/foo/bar
. 您也可以使用參數擴展來做到這一點:${target_path#/home/*/}
. 使用一個#
它會刪除最短的匹配前綴。