我正在尝试为 Linux 的 Windows 系统上的 Bash 编写一些东西,当通过函数传递时,将 ~ 转换为您的 Windows 用户目录winpath
。到目前为止,我能够检索 Windows 目录,并将其转换为 Unix 路径,并且我还能够/home/[username]/
找到~
.我遇到麻烦的地方是将这两者连接起来。
我有两个变量,如下所示:
$target_path=/home/jacob/Repositories
$user_path=/mnt/c/Users/Jacob
(实际上,我正在以编程方式检索它们,但我认为这没有什么区别。)
然后我将/home/jacob
其删除$target_path
,保持原样/Repositories
。
目标是$user_path
与修改后的结合$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
。如果我将字符串作为变量输入并尝试将它们组合起来,它就可以正常工作。唔。
答案1
是的,额外的回车符是你的问题。
进程替换会从进程的输出中删除最后的换行符(换行符),但如果cmd.exe
输出 CR-LF 对,则回车符会保留在 的末尾user_path
。打印时,CR 会使输出返回到打印时的行首。Repositories
长度相同,/mnt/c/Users
因此下面的斜杠在逻辑上对齐(相反,如果看起来有任何单词被破坏)。
您可以使用 删除 Bash 中的尾随 CR ${user_path%$'\r'}
。 (${var%pattern}
从变量中删除与模式匹配的后缀)
另外,我认为你的第二个 sed ( 's|^/home/\(.*\)/\(.*\)|/\2|'
) 有点太热心了,第一个.*
匹配它可以匹配的最长字符串,所以/home/user/foo/bar
会变成 just/bar
而不是/foo/bar
.您也可以通过参数扩展来做到这一点${target_path#/home/*/}
:对于 1,#
它会删除最短的匹配前缀。
答案2
感谢@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
}