Bash

在 Windows 上組合兩個變數會破壞它們

  • April 6, 2017

我正在嘗試在 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/*/}. 使用一個#它會刪除最短的匹配前綴。

引用自:https://unix.stackexchange.com/questions/355076