在 Windows 上组合两个变量会破坏它们

在 Windows 上组合两个变量会破坏它们

我正在尝试为 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
}

相关内容