Integreiddio gorchmynion Linux i Windows gan ddefnyddio PowerShell a WSL

Cwestiwn nodweddiadol gan ddatblygwyr Windows: “Pam nad oes o hyd <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?. P'un a yw'n swipe pwerus less neu offer cyfarwydd grep neu sed, Mae datblygwyr Windows eisiau mynediad hawdd i'r gorchmynion hyn yn eu gwaith dyddiol.

Is-system Windows ar gyfer Linux (WSL) wedi gwneud cam enfawr ymlaen yn hyn o beth. Mae'n caniatáu ichi ffonio gorchmynion Linux o Windows trwy ddirprwyo nhw wsl.exe (ee, wsl ls). Er bod hwn yn welliant sylweddol, mae'r opsiwn hwn yn dioddef o nifer o anfanteision.

  • Ychwanegiad hollbresennol wsl diflas ac annaturiol.
  • Nid yw llwybrau Windows mewn dadleuon bob amser yn gweithio oherwydd mae slaes yn cael eu dehongli fel nodau dianc yn hytrach na gwahanwyr cyfeiriadur.
  • Nid yw llwybrau Windows mewn dadleuon yn cael eu trosi i'r pwynt gosod cyfatebol yn WSL.
  • Nid yw gosodiadau diofyn yn cael eu parchu mewn proffiliau WSL gydag arallenwau a newidynnau amgylchedd.
  • Ni chefnogir cwblhau llwybr Linux.
  • Ni chefnogir cwblhau gorchymyn.
  • Ni chefnogir cwblhau dadl.

O ganlyniad, mae gorchmynion Linux yn cael eu trin fel dinasyddion ail ddosbarth o dan Windows - ac maent yn anoddach eu defnyddio na gorchmynion brodorol. Er mwyn cydraddoli eu hawliau, mae angen datrys y problemau a restrir.

Deunydd lapio swyddogaeth PowerShell

Gyda deunydd lapio swyddogaeth PowerShell, gallwn ychwanegu cwblhau gorchymyn a dileu'r angen am rhagddodiaid wsl, gan gyfieithu llwybrau Windows yn llwybrau WSL. Gofynion sylfaenol ar gyfer cregyn:

  • Ar gyfer pob gorchymyn Linux rhaid bod un peiriant lapio swyddogaeth gyda'r un enw.
  • Rhaid i'r gragen gydnabod y llwybrau Windows a basiwyd fel dadleuon a'u trosi i lwybrau WSL.
  • Dylai'r gragen alw wsl gyda'r gorchymyn Linux priodol i unrhyw fewnbwn piblinell a phasio unrhyw ddadleuon llinell orchymyn a drosglwyddir i'r swyddogaeth.

Gan y gellir cymhwyso'r patrwm hwn i unrhyw orchymyn, gallwn dynnu'r diffiniad o'r deunydd lapio hyn a'u cynhyrchu'n ddeinamig o restr o orchmynion i'w mewnforio.

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

Rhestr $command yn diffinio gorchmynion mewnforio. Yna byddwn yn cynhyrchu deunydd lapio swyddogaeth yn ddeinamig ar gyfer pob un ohonynt gan ddefnyddio'r gorchymyn Invoke-Expression (trwy ddileu yn gyntaf unrhyw arallenwau a fyddai'n gwrthdaro â'r swyddogaeth).

Mae'r swyddogaeth yn ailadrodd dros ddadleuon llinell orchymyn, yn pennu llwybrau Windows gan ddefnyddio gorchmynion Split-Path и Test-Pathac yna'n trosi'r llwybrau hyn yn llwybrau WSL. Rydyn ni'n rhedeg y llwybrau trwy swyddogaeth cynorthwyydd Format-WslArgument, y byddwn yn ei ddiffinio yn nes ymlaen. Mae'n dianc rhag cymeriadau arbennig fel bylchau a cromfachau a fyddai fel arall yn cael eu camddehongli.

Yn olaf, rydym yn cyfleu wsl mewnbwn piblinell ac unrhyw ddadleuon llinell orchymyn.

Gyda'r papurau lapio hyn gallwch chi ffonio'ch hoff orchmynion Linux mewn ffordd fwy naturiol heb ychwanegu rhagddodiad wsl a heb boeni am sut y caiff y llwybrau eu trosi:

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

Dangosir y set sylfaenol o orchmynion yma, ond gallwch greu cragen ar gyfer unrhyw orchymyn Linux trwy ei ychwanegu at y rhestr. Os ychwanegwch y cod hwn at eich proffil PowerShell, bydd y gorchmynion hyn ar gael i chi ym mhob sesiwn PowerShell, yn union fel gorchmynion brodorol!

Gosodiadau Diofyn

Yn Linux, mae'n gyffredin diffinio arallenwau a/neu newidynnau amgylchedd mewn proffiliau mewngofnodi, gan osod paramedrau rhagosodedig ar gyfer gorchmynion a ddefnyddir yn aml (er enghraifft, alias ls=ls -AFh neu export LESS=-i). Un o anfanteision dirprwyo trwy gragen nad yw'n rhyngweithiol wsl.exe - nad yw'r proffiliau wedi'u llwytho, felly nid yw'r opsiynau hyn ar gael yn ddiofyn (h.y. ls yn WSL a wsl ls yn ymddwyn yn wahanol gyda'r alias a ddiffinnir uchod).

Mae PowerShell yn darparu $PSDefaultParameterValues, mecanwaith safonol ar gyfer diffinio paramedrau rhagosodedig, ond dim ond ar gyfer cmdlets a swyddogaethau uwch. Wrth gwrs, gallwn wneud swyddogaethau uwch allan o'n cregyn, ond mae hyn yn cyflwyno cymhlethdodau diangen (er enghraifft, mae PowerShell yn cyfateb enwau paramedr rhannol (er enghraifft, -a yn cyfateb i -ArgumentList), a fydd yn gwrthdaro â gorchmynion Linux sy'n cymryd enwau rhannol fel dadleuon), ac nid y gystrawen ar gyfer diffinio gwerthoedd rhagosodedig fydd y mwyaf priodol (mae dadleuon diofyn yn gofyn am enw'r paramedr yn yr allwedd, nid dim ond yr enw gorchymyn) .

Fodd bynnag, gydag ychydig o addasiad i'n cregyn, gallwn weithredu model tebyg i $PSDefaultParameterValues, a galluogi opsiynau rhagosodedig ar gyfer gorchmynion 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 ' ')
    }
}

Trwy drosglwyddo $WslDefaultParameterValues i'r llinell orchymyn, rydym yn anfon paramedrau trwy wsl.exe. Mae'r canlynol yn dangos sut i ychwanegu cyfarwyddiadau at eich proffil PowerShell i ffurfweddu gosodiadau diofyn. Nawr gallwn ei wneud!

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

Gan fod y paramedrau wedi'u modelu ar ôl $PSDefaultParameterValues, Gallwch chi mae'n hawdd eu hanalluogi dros dro trwy osod yr allwedd "Disabled" i mewn i ystyr $true. Mantais ychwanegol tabl stwnsh ar wahân yw'r gallu i analluogi $WslDefaultParameterValues ar wahân i $PSDefaultParameterValues.

Cwblhau dadl

Mae PowerShell yn caniatáu ichi gofrestru trelars dadl gan ddefnyddio'r gorchymyn Register-ArgumentCompleter. Mae gan Bash bwerus offer cwblhau awto rhaglenadwy. Mae WSL yn caniatáu ichi ffonio bash o PowerShell. Os gallwn gofrestru cwblhau dadleuon ar gyfer ein deunydd lapio swyddogaeth PowerShell a bash galwadau i gynhyrchu'r cwblhad, byddwn yn cwblhau dadl lawn gyda'r un manwl gywirdeb â bash ei hun!

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

Mae'r cod ychydig yn drwchus heb ddeall rhai o swyddogaethau mewnol bash, ond yn y bôn yr hyn a wnawn yw hyn:

  • Cofrestru cyflawnwr dadl ar gyfer ein holl ddeunydd lapio swyddogaeth trwy basio rhestr $commands mewn paramedr -CommandName gyfer Register-ArgumentCompleter.
  • Rydym yn mapio pob gorchymyn i'r ffwythiant plisgyn y mae bash yn ei ddefnyddio ar gyfer awtolenwi (i ddiffinio manylebau awtogwblhau, defnyddiau bash $F, talfyriad am complete -F <FUNCTION>).
  • Trosi Dadleuon PowerShell $wordToComplete, $commandAst и $cursorPosition i mewn i'r fformat a ddisgwylir gan swyddogaethau awtolenwi bash yn unol â'r manylebau auto-gwblhau rhaglenadwy bash.
  • Rydym yn cyfansoddi llinell orchymyn i drosglwyddo iddi wsl.exe, sy'n sicrhau bod yr amgylchedd yn cael ei sefydlu'n gywir, yn galw'r swyddogaeth auto-gwblhau priodol, ac yn allbynnu'r canlyniadau mewn ffasiwn llinell wrth linell.
  • Yna rydym yn galw wsl gyda'r llinell orchymyn, rydym yn gwahanu'r allbwn fesul gwahanyddion llinell ac yn cynhyrchu ar gyfer pob un CompletionResults, eu didoli a dianc rhag cymeriadau fel bylchau a chromfachau a fyddai fel arall yn cael eu camddehongli.

O ganlyniad, bydd ein cregyn gorchymyn Linux yn defnyddio'r un awtomeiddio yn union â bash! Er enghraifft:

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

Mae pob awtogwblhau yn cyflenwi gwerthoedd sy'n benodol i'r ddadl flaenorol, gan ddarllen data cyfluniad fel gwesteiwyr hysbys o WSL!

<TAB> yn beicio drwy'r paramedrau. <Ctrl + пробел> yn dangos yr holl opsiynau sydd ar gael.

Hefyd, gan fod gennym ni awtolenwi bash bellach, gallwch chi awtolenwi llwybrau Linux yn uniongyrchol yn PowerShell!

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

Mewn achosion lle nad yw awtolenwi bash yn cynhyrchu unrhyw ganlyniadau, mae PowerShell yn dychwelyd i lwybrau Windows rhagosodedig y system. Felly, yn ymarferol, gallwch ddefnyddio'r ddau lwybr ar yr un pryd yn ôl eich disgresiwn.

Casgliad

Gan ddefnyddio PowerShell a WSL, gallwn integreiddio gorchmynion Linux i Windows fel cymwysiadau brodorol. Nid oes angen chwilio am Win32 builds neu gyfleustodau Linux neu dorri ar draws eich llif gwaith trwy fynd i gragen Linux. Dim ond gosod WSL, cyfluniad Proffil PowerShell и rhestrwch y gorchmynion rydych chi am eu mewnforio! Mae awtomeiddio cyfoethog ar gyfer paramedrau gorchymyn Linux a Windows a llwybrau ffeil yn ymarferoldeb nad yw hyd yn oed ar gael mewn gorchmynion brodorol Windows heddiw.

Mae'r cod ffynhonnell llawn a ddisgrifir uchod, yn ogystal â chanllawiau ychwanegol ar gyfer ei ymgorffori yn eich llif gwaith, ar gael yma.

Pa orchmynion Linux sydd fwyaf defnyddiol i chi? Pa bethau cyffredin eraill sydd ar goll wrth weithio yn Windows? Ysgrifennwch yn y sylwadau neu ar GitHub!

Ffynhonnell: hab.com

Ychwanegu sylw