Ngahijikeun paréntah Linux kana Windows nganggo PowerShell sareng WSL

Patarosan has ti pamekar Windows: "Naha masih aya <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?. Naha éta gesek anu kuat less atawa parabot akrab grep atawa sed, pamekar Windows hoyong aksés gampang kana paréntah ieu dina karya sapopoé maranéhna.

Windows Subsystem for Linux (WSL) geus nyieun hiji hambalan badag ka hareup dina hal ieu. Éta ngamungkinkeun anjeun nyauran paréntah Linux tina Windows ku proxying aranjeunna wsl.exe (contona. wsl ls). Sanajan ieu téh pamutahiran signifikan, pilihan ieu miboga sababaraha kalemahan.

  • Tambahan ubiquitous wsl tedious jeung teu wajar.
  • Jalur Windows dina argumen teu salawasna jalan sabab backslashes diinterpretasi salaku karakter ngewa tinimbang separators diréktori.
  • Jalur Windows dina argumen teu ditarjamahkeun kana titik Gunung pakait dina WSL.
  • Setélan standar teu dihargaan dina profil WSL kalawan aliases jeung variabel lingkungan.
  • Parantosan jalur Linux henteu dirojong.
  • Paréntah parantosan teu dirojong.
  • Ngarengsekeun argumen teu didukung.

Hasilna, paréntah Linux diperlakukeun sapertos warga kelas kadua dina Windows-sareng langkung hese dianggo tibatan paréntah asli. Pikeun equalize hak maranéhanana, perlu pikeun ngajawab masalah didaptarkeun.

pambungkus fungsi PowerShell

Kalayan pembungkus fungsi PowerShell, urang tiasa nambihan paréntah parantosan sareng ngaleungitkeun kabutuhan awalan wsl, narjamahkeun jalur Windows kana jalur WSL. Syarat dasar pikeun cangkang:

  • Pikeun unggal paréntah Linux kedah aya hiji bungkus fungsi sareng nami anu sami.
  • Cangkang kedah ngakuan jalur Windows anu disalurkeun salaku argumen sareng ngarobih kana jalur WSL.
  • Cangkang kedah nelepon wsl kalawan paréntah Linux Ubuntu luyu kana sagala input pipeline sarta ngalirkeun sagala argumen garis paréntah dibikeun ka fungsi.

Kusabab pola ieu tiasa diterapkeun kana paréntah naon waé, urang tiasa abstrak definisi bungkus ieu sareng ngahasilkeun sacara dinamis tina daptar paréntah pikeun diimpor.

# The commands to import.
$commands = "awk", "emacs", "grep", "head", "less", "ls", "man", "sed", "seq", "ssh", "tail", "vim"
 
# Register a function for each command.
$commands | ForEach-Object { Invoke-Expression @"
Remove-Alias $_ -Force -ErrorAction Ignore
function global:$_() {
    for (`$i = 0; `$i -lt `$args.Count; `$i++) {
        # If a path is absolute with a qualifier (e.g. C:), run it through wslpath to map it to the appropriate mount point.
        if (Split-Path `$args[`$i] -IsAbsolute -ErrorAction Ignore) {
            `$args[`$i] = Format-WslArgument (wsl.exe wslpath (`$args[`$i] -replace "", "/"))
        # If a path is relative, the current working directory will be translated to an appropriate mount point, so just format it.
        } elseif (Test-Path `$args[`$i] -ErrorAction Ignore) {
            `$args[`$i] = Format-WslArgument (`$args[`$i] -replace "", "/")
        }
    }
 
    if (`$input.MoveNext()) {
        `$input.Reset()
        `$input | wsl.exe $_ (`$args -split ' ')
    } else {
        wsl.exe $_ (`$args -split ' ')
    }
}
"@
}

daptar $command ngahartikeun paréntah impor. Urang lajeng dinamis ngahasilkeun wrapper fungsi pikeun tiap sahijina ngagunakeun paréntah Invoke-Expression (ku mimitina ngaleungitkeun aliases anu bakal konflik jeung fungsi).

Fungsina iterates leuwih argumen baris paréntah, nangtukeun jalur Windows maké paréntah Split-Path и Test-Pathlajeng ngarobah jalur ieu ka jalur WSL. Urang ngajalankeun jalur ngaliwatan fungsi helper Format-WslArgument, nu urang tangtukeun engké. Ieu escapes karakter husus kayaning spasi na kurung nu disebutkeun bakal misinterpreted.

Tungtungna, urang nepikeun wsl input pipeline sareng naon waé argumen garis paréntah.

Kalayan bungkus ieu anjeun tiasa nyauran paréntah Linux karesep anjeun ku cara anu langkung alami tanpa nambihan awalan wsl sareng tanpa hariwang ngeunaan kumaha jalanna dirobih:

  • man bash
  • less -i $profile.CurrentUserAllHosts
  • ls -Al C:Windows | less
  • grep -Ein error *.log
  • tail -f *.log

Susunan paréntah dasar dipidangkeun di dieu, tapi anjeun tiasa nyiptakeun cangkang pikeun paréntah Linux naon waé ku cara nambihanana kana daptar. Lamun anjeun tambahkeun kode ieu anjeun keureutan PowerShell, paréntah ieu bakal sayogi pikeun anjeun dina unggal sési PowerShell, sapertos paréntah asli!

Setélan standar

Dina Linux, geus ilahar pikeun nangtukeun landian jeung/atawa variabel lingkungan dina profil login, netepkeun parameter standar pikeun paréntah nu mindeng dipaké (contona, alias ls=ls -AFh atawa export LESS=-i). Salah sahiji kalemahan proxying ngaliwatan cangkang non-interaktif wsl.exe - yén propil henteu dimuat, janten pilihan ieu henteu sayogi sacara standar (ie. ls di WSL jeung wsl ls bakal kalakuanana béda jeung alias didefinisikeun di luhur).

PowerShell nyadiakeun $PSDefaultParameterValues, mékanisme standar pikeun nangtukeun parameter standar, tapi ngan pikeun cmdlet jeung fungsi canggih. Tangtosna, urang tiasa ngadamel fungsi canggih tina cangkang urang, tapi ieu ngenalkeun komplikasi anu teu perlu (contona, PowerShell ngahubungkeun nami parameter parsial (contona, -a correlates kalawan -ArgumentList), anu bakal bertentangan sareng paréntah Linux anu nyandak nami parsial salaku argumen), sareng sintaksis pikeun netepkeun nilai standar moal janten anu paling pas (argumen standar butuh nami parameter dina konci, sanés ngan ukur nami paréntah) .

Nanging, ku modifikasi sakedik kana cangkang urang, urang tiasa nerapkeun modél anu sami $PSDefaultParameterValues, sareng aktipkeun pilihan standar pikeun paréntah Linux!

function global:$_() {
    …
 
    `$defaultArgs = ((`$WslDefaultParameterValues.$_ -split ' '), "")[`$WslDefaultParameterValues.Disabled -eq `$true]
    if (`$input.MoveNext()) {
        `$input.Reset()
        `$input | wsl.exe $_ `$defaultArgs (`$args -split ' ')
    } else {
        wsl.exe $_ `$defaultArgs (`$args -split ' ')
    }
}

Ngaliwatan $WslDefaultParameterValues kana garis paréntah, urang ngirim parameter via wsl.exe. Di handap ieu nunjukkeun kumaha cara nambihan pitunjuk kana profil PowerShell anjeun pikeun ngonpigurasikeun setélan standar. Ayeuna urang tiasa ngalakukeun!

$WslDefaultParameterValues["grep"] = "-E"
$WslDefaultParameterValues["less"] = "-i"
$WslDefaultParameterValues["ls"] = "-AFh --group-directories-first"

Kusabab parameter dimodelkeun sanggeus $PSDefaultParameterValuesanjeun tiasa éta gampang pikeun nganonaktipkeun aranjeunna samentara ku masang konci "Disabled" kana harti $true. Kauntungan tambahan tina tabel hash anu misah nyaéta kamampuan nganonaktipkeun $WslDefaultParameterValues misah ti $PSDefaultParameterValues.

Arguméntasi réngsé

PowerShell ngamungkinkeun anjeun ngadaptar gandeng argumen nganggo paréntah Register-ArgumentCompleter. Bash boga kakuatan parabot otomatis-parantosan programmable. WSL ngidinan Anjeun pikeun nelepon bash ti PowerShell. Upami urang tiasa ngadaptarkeun penyelesaian argumen pikeun pembungkus fungsi PowerShell urang sareng nelepon bash pikeun ngahasilkeun parantosan, urang nampi lengkep argumen lengkep sareng presisi anu sami sareng bash sorangan!

# Register an ArgumentCompleter that shims bash's programmable completion.
Register-ArgumentCompleter -CommandName $commands -ScriptBlock {
    param($wordToComplete, $commandAst, $cursorPosition)
 
    # Map the command to the appropriate bash completion function.
    $F = switch ($commandAst.CommandElements[0].Value) {
        {$_ -in "awk", "grep", "head", "less", "ls", "sed", "seq", "tail"} {
            "_longopt"
            break
        }
 
        "man" {
            "_man"
            break
        }
 
        "ssh" {
            "_ssh"
            break
        }
 
        Default {
            "_minimal"
            break
        }
    }
 
    # Populate bash programmable completion variables.
    $COMP_LINE = "`"$commandAst`""
    $COMP_WORDS = "('$($commandAst.CommandElements.Extent.Text -join "' '")')" -replace "''", "'"
    for ($i = 1; $i -lt $commandAst.CommandElements.Count; $i++) {
        $extent = $commandAst.CommandElements[$i].Extent
        if ($cursorPosition -lt $extent.EndColumnNumber) {
            # The cursor is in the middle of a word to complete.
            $previousWord = $commandAst.CommandElements[$i - 1].Extent.Text
            $COMP_CWORD = $i
            break
        } elseif ($cursorPosition -eq $extent.EndColumnNumber) {
            # The cursor is immediately after the current word.
            $previousWord = $extent.Text
            $COMP_CWORD = $i + 1
            break
        } elseif ($cursorPosition -lt $extent.StartColumnNumber) {
            # The cursor is within whitespace between the previous and current words.
            $previousWord = $commandAst.CommandElements[$i - 1].Extent.Text
            $COMP_CWORD = $i
            break
        } elseif ($i -eq $commandAst.CommandElements.Count - 1 -and $cursorPosition -gt $extent.EndColumnNumber) {
            # The cursor is within whitespace at the end of the line.
            $previousWord = $extent.Text
            $COMP_CWORD = $i + 1
            break
        }
    }
 
    # Repopulate bash programmable completion variables for scenarios like '/mnt/c/Program Files'/<TAB> where <TAB> should continue completing the quoted path.
    $currentExtent = $commandAst.CommandElements[$COMP_CWORD].Extent
    $previousExtent = $commandAst.CommandElements[$COMP_CWORD - 1].Extent
    if ($currentExtent.Text -like "/*" -and $currentExtent.StartColumnNumber -eq $previousExtent.EndColumnNumber) {
        $COMP_LINE = $COMP_LINE -replace "$($previousExtent.Text)$($currentExtent.Text)", $wordToComplete
        $COMP_WORDS = $COMP_WORDS -replace "$($previousExtent.Text) '$($currentExtent.Text)'", $wordToComplete
        $previousWord = $commandAst.CommandElements[$COMP_CWORD - 2].Extent.Text
        $COMP_CWORD -= 1
    }
 
    # Build the command to pass to WSL.
    $command = $commandAst.CommandElements[0].Value
    $bashCompletion = ". /usr/share/bash-completion/bash_completion 2> /dev/null"
    $commandCompletion = ". /usr/share/bash-completion/completions/$command 2> /dev/null"
    $COMPINPUT = "COMP_LINE=$COMP_LINE; COMP_WORDS=$COMP_WORDS; COMP_CWORD=$COMP_CWORD; COMP_POINT=$cursorPosition"
    $COMPGEN = "bind `"set completion-ignore-case on`" 2> /dev/null; $F `"$command`" `"$wordToComplete`" `"$previousWord`" 2> /dev/null"
    $COMPREPLY = "IFS=`$'n'; echo `"`${COMPREPLY[*]}`""
    $commandLine = "$bashCompletion; $commandCompletion; $COMPINPUT; $COMPGEN; $COMPREPLY" -split ' '
 
    # Invoke bash completion and return CompletionResults.
    $previousCompletionText = ""
    (wsl.exe $commandLine) -split 'n' |
    Sort-Object -Unique -CaseSensitive |
    ForEach-Object {
        if ($wordToComplete -match "(.*=).*") {
            $completionText = Format-WslArgument ($Matches[1] + $_) $true
            $listItemText = $_
        } else {
            $completionText = Format-WslArgument $_ $true
            $listItemText = $completionText
        }
 
        if ($completionText -eq $previousCompletionText) {
            # Differentiate completions that differ only by case otherwise PowerShell will view them as duplicate.
            $listItemText += ' '
        }
 
        $previousCompletionText = $completionText
        [System.Management.Automation.CompletionResult]::new($completionText, $listItemText, 'ParameterName', $completionText)
    }
}
 
# Helper function to escape characters in arguments passed to WSL that would otherwise be misinterpreted.
function global:Format-WslArgument([string]$arg, [bool]$interactive) {
    if ($interactive -and $arg.Contains(" ")) {
        return "'$arg'"
    } else {
        return ($arg -replace " ", " ") -replace "([()|])", ('$1', '`$1')[$interactive]
    }
}

Kodena rada padet tanpa ngartos sababaraha fungsi internal bash, tapi dasarna anu urang lakukeun nyaéta:

  • Ngadaptarkeun hiji argumen completer pikeun sakabéh wrappers fungsi urang ku ngalirkeun daptar $commands dina parameter -CommandName keur Register-ArgumentCompleter.
  • Urang peta unggal paréntah kana fungsi cangkang nu bash ngagunakeun pikeun autocompletion (pikeun nangtukeun spésifikasi autocompletion, bash migunakeun $F, singketan pikeun complete -F <FUNCTION>).
  • Ngarobah PowerShell Argumen $wordToComplete, $commandAst и $cursorPosition kana format anu dipiharep ku fungsi autocompletion bash numutkeun spésifikasi programmable otomatis-parantosan bash.
  • Urang nyusun garis paréntah pikeun mindahkeun ka wsl.exe, nu ensures yén lingkungan geus nyetél neuleu, nelepon fungsi otomatis-parantosan luyu, sarta outputs hasil dina fashion garis-demi-garis.
  • Teras we nelepon wsl kalawan garis paréntah, urang misahkeun kaluaran ku separators garis tur ngahasilkeun pikeun tiap CompletionResults, asihan aranjeunna sarta escaping karakter kayaning spasi na kurung nu disebutkeun bakal misinterpreted.

Hasilna, cangkang paréntah Linux urang bakal nganggo autocompletion anu sami sareng bash! Salaku conto:

  • ssh -c <TAB> -J <TAB> -m <TAB> -O <TAB> -o <TAB> -Q <TAB> -w <TAB> -b <TAB>

Unggal autocompletion nyadiakeun nilai husus pikeun argumen saméméhna, maca data konfigurasi kayaning host dipikawanoh ti WSL!

<TAB> bakal siklus ngaliwatan parameter. <Ctrl + пробел> bakal nembongkeun sagala pilihan sadia.

Tambih Deui, saprak urang ayeuna gaduh autocompletion bash, anjeun tiasa autocomplete jalur Linux langsung di PowerShell!

  • less /etc/<TAB>
  • ls /usr/share/<TAB>
  • vim ~/.bash<TAB>

Dina kasus dimana autocompletion bash henteu ngahasilkeun hasil naon waé, PowerShell dibalikkeun ka jalur Windows standar sistem. Ku kituna, dina prakna, anjeun tiasa sakaligus nganggo duanana jalur dina kawijaksanaan anjeun.

kacindekan

Ngagunakeun PowerShell sareng WSL, urang tiasa ngahijikeun paréntah Linux kana Windows salaku aplikasi asli. Teu kedah milarian Win32 ngawangun atanapi utilitas Linux atanapi ngaganggu alur kerja anjeun ku jalan ka cangkang Linux. Ngan masang WSL, ngonpigurasikeun Propil PowerShell и daptar paréntah anu anjeun hoyong impor! Autocompletion beunghar pikeun parameter paréntah Linux sareng Windows sareng jalur file mangrupikeun fungsionalitas anu henteu sayogi dina paréntah Windows asli ayeuna.

Kode sumber lengkep ditétélakeun di luhur, kitu ogé tungtunan tambahan pikeun incorporating kana workflow anjeun, geus sadia di dieu.

Paréntah Linux mana anu anjeun mendakan paling mangpaat? Naon hal umum anu leungit nalika damel di Windows? Tulis dina komentar atawa dina GitHub!

sumber: www.habr.com

Tambahkeun komentar