Kuunganisha amri za Linux kwenye Windows kwa kutumia PowerShell na WSL

Swali la kawaida kutoka kwa watengenezaji wa Windows: "Kwa nini bado hakuna <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?. Kama ni swipe yenye nguvu less au zana zinazojulikana grep au sed, Wasanidi wa Windows wanataka ufikiaji rahisi wa amri hizi katika kazi zao za kila siku.

Mfumo mdogo wa Windows wa Linux (WSL) imepiga hatua kubwa katika suala hili. Inakuruhusu kupiga amri za Linux kutoka Windows kwa kuwawakilisha kupitia wsl.exe (k.v. wsl ls) Ingawa hii ni uboreshaji mkubwa, chaguo hili linakabiliwa na idadi ya hasara.

  • Nyongeza ya kila mahali wsl ya kuchosha na isiyo ya asili.
  • Njia za Windows katika hoja hazifanyi kazi kila wakati kwa sababu mikwaju ya nyuma inafasiriwa kama herufi za kutoroka badala ya vitenganishi vya saraka.
  • Njia za Windows katika hoja hazitafsiriwi kwa sehemu inayolingana ya mlima katika WSL.
  • Mipangilio chaguo-msingi haiheshimiwi katika wasifu wa WSL na lakabu na anuwai za mazingira.
  • Ukamilishaji wa njia ya Linux hautumiki.
  • Ukamilishaji wa amri haukubaliwi.
  • Ukamilishaji wa hoja haukubaliwi.

Kwa hivyo, amri za Linux zinachukuliwa kama raia wa daraja la pili chini ya Windows-na ni vigumu zaidi kutumia kuliko amri za asili. Ili kusawazisha haki zao, ni muhimu kutatua matatizo yaliyoorodheshwa.

Vifungashio vya kazi ya PowerShell

Kwa vifungashio vya utendakazi vya PowerShell, tunaweza kuongeza ukamilishaji wa amri na kuondoa hitaji la viambishi awali wsl, kutafsiri njia za Windows kuwa njia za WSL. Mahitaji ya kimsingi kwa ganda:

  • Kwa kila amri ya Linux lazima kuwe na karatasi moja ya kukokotoa yenye jina moja.
  • Ganda lazima litambue njia za Windows zilizopitishwa kama hoja na kuzibadilisha kuwa njia za WSL.
  • Ganda inapaswa kupiga simu wsl na amri inayofaa ya Linux kwa pembejeo yoyote ya bomba na kupitisha hoja zozote za safu ya amri zilizopitishwa kwenye chaguo la kukokotoa.

Kwa kuwa muundo huu unaweza kutumika kwa amri yoyote, tunaweza kutoa ufafanuzi wa kanga hizi na kuzizalisha kwa nguvu kutoka kwa orodha ya amri za kuagiza.

# 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 ' ')
    }
}
"@
}

Orodha ya $command inafafanua amri za uingizaji. Kisha tunatoa kiboreshaji cha kazi kwa kila mmoja wao kwa kutumia amri Invoke-Expression (kwa kuondoa kwanza lakabu zozote ambazo zingegongana na chaguo la kukokotoa).

Kazi inarudia juu ya hoja za mstari wa amri, huamua njia za Windows kwa kutumia amri Split-Path и Test-Pathna kisha kubadilisha njia hizi kuwa njia za WSL. Tunaendesha njia kupitia kazi ya msaidizi Format-WslArgument, ambayo tutafafanua baadaye. Huepuka herufi maalum kama vile nafasi na mabano ambayo vinginevyo yangefasiriwa vibaya.

Hatimaye, tunafikisha wsl pembejeo ya bomba na hoja zozote za mstari wa amri.

Ukiwa na kanga hizi unaweza kuita amri zako za Linux uzipendazo kwa njia ya asili zaidi bila kuongeza kiambishi awali wsl na bila kuwa na wasiwasi juu ya jinsi njia zinavyobadilishwa:

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

Seti ya msingi ya amri imeonyeshwa hapa, lakini unaweza kuunda shell kwa amri yoyote ya Linux kwa kuiongeza tu kwenye orodha. Ukiongeza nambari hii kwenye yako wasifu PowerShell, amri hizi zitapatikana kwako katika kila kipindi cha PowerShell, kama tu amri za asili!

Mipangilio Chaguomsingi

Katika Linux, ni kawaida kufafanua lakabu na/au vigeu vya mazingira katika wasifu wa kuingia, kuweka vigezo chaguo-msingi kwa amri zinazotumiwa mara kwa mara (kwa mfano, alias ls=ls -AFh au export LESS=-i) Moja ya hasara za kufanya seva mbadala kupitia ganda lisiloingiliana wsl.exe - kwamba wasifu haujapakiwa, kwa hivyo chaguzi hizi hazipatikani kwa chaguo-msingi (i.e. ls katika WSL na wsl ls itatenda tofauti na lakabu iliyofafanuliwa hapo juu).

PowerShell hutoa $PSDefaultParameterValues, utaratibu wa kawaida wa kufafanua vigezo chaguo-msingi, lakini tu kwa cmdlets na vitendaji vya juu. Kwa kweli, tunaweza kufanya utendakazi wa hali ya juu kutoka kwa makombora yetu, lakini hii inaleta shida zisizo za lazima (kwa mfano, PowerShell inaunganisha majina ya parameta (kwa mfano, -a inahusiana na -ArgumentList), ambayo itakinzana na amri za Linux ambazo huchukua majina ya sehemu kama hoja), na syntax ya kufafanua maadili chaguo-msingi haitakuwa sahihi zaidi (hoja chaguo-msingi zinahitaji jina la kigezo kwenye ufunguo, sio tu jina la amri) .

Walakini, kwa marekebisho kidogo kwa makombora yetu, tunaweza kutekeleza mfano sawa na $PSDefaultParameterValues, na uwashe chaguzi chaguo-msingi kwa amri za 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 ' ')
    }
}

Kupita $WslDefaultParameterValues kwa mstari wa amri, tunatuma vigezo kupitia wsl.exe. Ifuatayo inaonyesha jinsi ya kuongeza maagizo kwenye wasifu wako wa PowerShell ili kusanidi mipangilio chaguomsingi. Sasa tunaweza kuifanya!

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

Kwa kuwa vigezo vinatengenezwa baada ya $PSDefaultParameterValuesunaweza ni rahisi kuzizima kwa muda kwa kusakinisha ufunguo "Disabled" kwenye maana $true. Faida ya ziada ya jedwali tofauti la hashi ni uwezo wa kuzima $WslDefaultParameterValues tofauti na $PSDefaultParameterValues.

Kukamilika kwa hoja

PowerShell hukuruhusu kusajili trela za hoja kwa kutumia amri Register-ArgumentCompleter. Bash ina nguvu zana zinazoweza kupangwa za kukamilisha kiotomatiki. WSL hukuruhusu kupiga simu bash kutoka PowerShell. Ikiwa tunaweza kusajili ukamilishaji wa hoja kwa vifungashio vyetu vya utendaji wa PowerShell na kupiga simu bash ili kutoa ukamilishaji, tunapata ukamilisho kamili wa hoja kwa usahihi sawa na bash yenyewe!

# 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]
    }
}

Nambari ni mnene kidogo bila kuelewa baadhi ya kazi za ndani za bash, lakini kimsingi tunachofanya ni hii:

  • Kusajili kikamilisha hoja kwa vifungashio vyetu vyote vya kukokotoa kwa kupitisha orodha $commands katika parameter -CommandName kwa Register-ArgumentCompleter.
  • Tunapanga kila amri kwa kazi ya ganda ambayo bash hutumia kukamilisha kiotomatiki (kufafanua uainishaji wa ukamilishaji kiotomatiki, matumizi ya bash $F, kifupi cha complete -F <FUNCTION>).
  • Kubadilisha Hoja za PowerShell $wordToComplete, $commandAst и $cursorPosition katika umbizo linalotarajiwa na kazi za ukamilishaji otomatiki za bash kulingana na vipimo ukamilishaji otomatiki unaoweza kuratibiwa bash.
  • Tunatunga mstari wa amri ili kuhamisha wsl.exe, ambayo inahakikisha kwamba mazingira yamewekwa kwa usahihi, huita kazi inayofaa ya kukamilisha-otomatiki, na hutoa matokeo kwa mtindo wa mstari kwa mstari.
  • Kisha tunaita wsl kwa mstari wa amri, tunatenganisha pato kwa watenganishaji wa mstari na kuzalisha kwa kila mmoja CompletionResults, kuzipanga na kuepuka herufi kama vile nafasi na mabano ambayo vinginevyo yangetafsiriwa vibaya.

Kama matokeo, makombora yetu ya amri ya Linux yatatumia ukamilishaji otomatiki sawa na bash! Kwa mfano:

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

Kila ukamilishaji kiotomatiki hutoa thamani mahususi kwa hoja iliyotangulia, kusoma data ya usanidi kama vile wapangishi wanaojulikana kutoka WSL!

<TAB> itazunguka kupitia vigezo. <Ctrl + пробел> itaonyesha chaguzi zote zinazopatikana.

Pamoja, kwa kuwa sasa tuna ukamilishaji otomatiki wa bash, unaweza kukamilisha kiotomatiki njia za Linux moja kwa moja kwenye PowerShell!

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

Katika hali ambapo ukamilishaji otomatiki wa bash hautoi matokeo yoyote, PowerShell hurudi kwenye njia chaguo-msingi za mfumo wa Windows. Kwa hivyo, kwa mazoezi, unaweza kutumia wakati huo huo njia zote mbili kwa hiari yako.

Hitimisho

Kwa kutumia PowerShell na WSL, tunaweza kuunganisha amri za Linux kwenye Windows kama programu asilia. Hakuna haja ya kutafuta Win32 builds au huduma za Linux au kukatiza utendakazi wako kwa kwenda kwenye ganda la Linux. Tu sakinisha WSL, sanidi Wasifu wa PowerShell и orodhesha amri unazotaka kuagiza! Ukamilishaji mwingi wa kiotomatiki wa vigezo vya amri vya Linux na Windows na njia za faili ni utendakazi ambao haupatikani hata katika amri asili za Windows leo.

Msimbo kamili wa chanzo uliofafanuliwa hapo juu, pamoja na miongozo ya ziada ya kuijumuisha katika utendakazi wako, inapatikana hapa.

Ni amri gani za Linux unaona kuwa muhimu zaidi? Ni vitu gani vingine vya kawaida vinakosekana wakati wa kufanya kazi kwenye Windows? Andika kwenye maoni au kwenye GitHub!

Chanzo: mapenzi.com

Kuongeza maoni