Combinando duas variáveis os manipula no Windows

2

Estou tentando escrever algo para o Bash no Windows System for Linux que converta ~ no diretório de usuários do Windows quando passar por uma função winpath . Até agora, consegui recuperar o diretório do Windows e convertê-lo em um caminho Unix, e também consegui encontrar a peça depois de /home/[username]/ , que obtém a saída em ~ . Onde eu estou correndo para o problema é na concatenação destes dois.

Eu tenho duas variáveis, assim:

$target_path=/home/jacob/Repositories
$user_path=/mnt/c/Users/Jacob

(Na realidade, estou recuperando-os programaticamente, mas não acho que isso faça diferença.)

Eu, então, removemos /home/jacob de $target_path , deixando apenas /Repositories .

O objetivo é combinar $user_path com o $target_path modificado, para que seja gerado como:

/mnt/c/Users/Jacob/Repositories

Para fazer isso, estou simplesmente fazendo:

target_path=$user_path$target_path

Mas o que está acontecendo, por algum motivo, é a saída como:

/RepositoriesJacob

Isso não faz sentido, porque quando eu envio $user_path por conta própria, está correto, e quando eu saio $target_path por conta própria, também está correto. Então, é algo em combinar esses dois que está estragando tudo.

As linhas relevantes são 8-20 em esta essência (código completo colado abaixo).

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\E/|')
        # remove /home/[username]/ from $target_path
        target_path=$(echo "$target_path" | sed -e 's|^/home/\(.*\)/\(.*\)|/|')
        # 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:\\E|' -e 's|/|\|g')
    else
        # return the user's home directory if a Unix path was parsed
        echo $user_path
    fi
}

EDIT: Ok, então isso é estranho ... Tentando isso em um Mac, funciona bem. Talvez seja algum bug com o WSL?

EDIT 2: Depois de mais testes, parece que tem a ver com a combinação das duas saídas sed . Se eu digitar as strings como variáveis e tentar combiná-las, funcionará bem. Hmm.

    
por JacobTheDev 31.03.2017 / 16:29

2 respostas

1

Graças à ajuda do @steeldriver, consegui descobrir isso! Estranhamente, era um problema com os finais de linha do Windows, embora houvesse apenas uma linha sendo produzida pelo CMD. A solução foi converter a saída do CMD para o estilo Unix e isso resolveu o problema! Aqui está o meu código final:

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\E/|' -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/*/\(.*\)|/|')
        # 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:\E|' -e 's|/|\|g')
    else
        # return the user's home directory if a Unix path was parsed
        echo $user_path
    fi
}
    
por 31.03.2017 / 17:02
2

Sim, o retorno extra de transporte é o seu problema.

A substituição do processo remove a nova linha final (avanço de linha) da saída do processo, mas no caso de cmd.exe produzir um par CR-LF, o retorno do carro permanece no final de user_path . Quando impresso, o CR faz com que a saída retorne ao início da linha quando impressa. Repositories tem o mesmo comprimento que /mnt/c/Users , de forma que a barra seguinte se alinha em um lugar um pouco lógico (em vez disso, se parecer uma palavra foi quebrada).

Você pode remover um CR à direita no Bash com ${user_path%$'\r'} . ( ${var%pattern} remove um sufixo que corresponde ao padrão da variável)

Além disso, acho que seu segundo sed ( 's|^/home/\(.*\)/\(.*\)|/|' ) é um pouco zeloso demais, o primeiro .* corresponde à maior sequência possível, portanto, /home/user/foo/bar se transformaria em apenas /bar em vez de /foo/bar . Você poderia fazer isso com as expansões de parâmetro também: ${target_path#/home/*/} . Com um # , remove o prefixo correspondente mais curto.

    
por 31.03.2017 / 17:19