L-integrazzjoni tal-kmandi tal-Linux fil-Windows billi tuża PowerShell u WSL

Mistoqsija tipika mill-iżviluppaturi tal-Windows: "Għaliex għad hemm l-ebda <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?. Kemm jekk huwa swipe qawwija less jew għodod familjari grep jew sed, L-iżviluppaturi tal-Windows iridu aċċess faċli għal dawn il-kmandi fix-xogħol tagħhom ta 'kuljum.

Subsistema tal-Windows għal Linux (WSL) għamel pass kbir 'il quddiem f'dan ir-rigward. Jippermettilek issejjaħ kmandi tal-Linux mill-Windows billi tipprokurahom wsl.exe (eż., wsl ls). Għalkemm dan huwa titjib sinifikanti, din l-għażla tbati minn numru ta 'żvantaġġi.

  • Żieda kullimkien wsl tedjanti u mhux naturali.
  • Il-mogħdijiet tal-Windows fl-argumenti mhux dejjem jaħdmu minħabba li l-backslashes huma interpretati bħala karattri ta 'ħarba aktar milli separaturi tad-direttorju.
  • Il-mogħdijiet tal-Windows fl-argumenti mhumiex tradotti għall-punt tal-muntaġġ korrispondenti fil-WSL.
  • Is-settings awtomatiċi mhumiex rispettati fil-profili WSL b'psewdonimi u varjabbli ambjentali.
  • It-tlestija tal-passaġġ tal-Linux mhix appoġġata.
  • It-tlestija tal-kmand mhix appoġġata.
  • It-tlestija tal-argument mhix appoġġata.

Bħala riżultat, il-kmandi tal-Linux jiġu ttrattati bħal ċittadini tat-tieni klassi taħt il-Windows—u huma aktar diffiċli biex jintużaw mill-kmandi indiġeni. Biex jiġu ndaqs id-drittijiet tagħhom, huwa meħtieġ li jissolvew il-problemi elenkati.

Gżejjer tal-funzjoni PowerShell

Bil-wrappers tal-funzjoni PowerShell, nistgħu nżidu t-tlestija tal-kmand u neliminaw il-ħtieġa għal prefissi wsl, tittraduċi mogħdijiet tal-Windows f'mogħdijiet WSL. Rekwiżiti bażiċi għall-qxur:

  • Għal kull kmand tal-Linux irid ikun hemm wrapper funzjoni waħda bl-istess isem.
  • Il-qoxra trid tagħraf il-mogħdijiet tal-Windows mgħoddija bħala argumenti u tikkonvertihom għal mogħdijiet WSL.
  • Il-qoxra għandha ssejjaħ wsl bil-kmand Linux xieraq għal kwalunkwe input tal-pipeline u jgħaddi kwalunkwe argument tal-linja tal-kmand mgħoddi lill-funzjoni.

Peress li dan il-mudell jista 'jiġi applikat għal kwalunkwe kmand, nistgħu nastrattaw id-definizzjoni ta' dawn it-tgeżwir u niġġenerawhom b'mod dinamiku minn lista ta 'kmandi għall-importazzjoni.

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

Lista $command jiddefinixxi l-kmandi tal-importazzjoni. Aħna mbagħad niġġeneraw b'mod dinamiku wrapper tal-funzjoni għal kull wieħed minnhom bl-użu tal-kmand Invoke-Expression (billi l-ewwel jitneħħew kwalunkwe psewdonimi li jkunu f'kunflitt mal-funzjoni).

Il-funzjoni ttenni fuq argumenti tal-linja tal-kmand, tiddetermina l-mogħdijiet tal-Windows billi tuża kmandi Split-Path и Test-Pathu mbagħad tikkonverti dawn il-mogħdijiet għal mogħdijiet WSL. Aħna nmexxu l-mogħdijiet permezz ta 'funzjoni helper Format-WslArgument, li ser niddefinixxu aktar tard. Jaħrab karattri speċjali bħal spazji u parentesi li inkella jkunu interpretati ħażin.

Fl-aħħarnett, inwasslu wsl input tal-pipeline u kwalunkwe argument tal-linja tal-kmand.

B'dawn it-tgeżwir tista' ċċempel il-kmandi favoriti tiegħek tal-Linux b'mod aktar naturali mingħajr ma żżid prefiss wsl u mingħajr ma tinkwieta dwar kif il-mogħdijiet huma konvertiti:

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

Is-sett bażiku ta 'kmandi jidher hawn, iżda tista' toħloq qoxra għal kwalunkwe kmand Linux billi sempliċement iżżidha mal-lista. Jekk iżżid dan il-kodiċi ma' tiegħek profil PowerShell, dawn il-kmandi se jkunu disponibbli għalik f'kull sessjoni ta' PowerShell, bħall-kmandi indiġeni!

Settings Default

Fil-Linux, huwa komuni li jiġu definiti psewdonimi u/jew varjabbli tal-ambjent fil-profili tal-login, billi jiġu stabbiliti parametri default għall-kmandi użati ta’ spiss (pereżempju, alias ls=ls -AFh jew export LESS=-i). Wieħed mill-iżvantaġġi ta 'proxying permezz ta' qoxra mhux interattiva wsl.exe - li l-profili ma jkunux mgħobbija, għalhekk dawn l-għażliet mhumiex disponibbli awtomatikament (i.e. ls fil-WSL u wsl ls se jġib ruħu b'mod differenti bl-alias definit hawn fuq).

PowerShell jipprovdi $PSDefaultParameterValues, mekkaniżmu standard għad-definizzjoni tal-parametri default, iżda biss għal cmdlets u funzjonijiet avvanzati. Naturalment, nistgħu nagħmlu funzjonijiet avvanzati mill-qxur tagħna, iżda dan jintroduċi kumplikazzjonijiet mhux meħtieġa (per eżempju, PowerShell jikkorrelata ismijiet ta' parametri parzjali (per eżempju, -a jikkorrelata ma' -ArgumentList), li se jkun f'kunflitt mal-kmandi tal-Linux li jieħdu ismijiet parzjali bħala argumenti), u s-sintassi għad-definizzjoni tal-inadempjenzi mhux se tkun l-aktar xierqa (id-definizzjoni tal-argumenti awtomatiċi teħtieġ l-isem tal-parametru fiċ-ċavetta, mhux biss l-isem tal-kmand).

Madankollu, b'modifika żgħira għall-qxur tagħna, nistgħu nimplimentaw mudell simili għal $PSDefaultParameterValues, u jippermettu għażliet default għall-kmandi tal-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 ' ')
    }
}

Għaddi $WslDefaultParameterValues għal-linja tal-kmand, aħna nibagħtu parametri permezz wsl.exe. Dan li ġej juri kif iżżid struzzjonijiet mal-profil PowerShell tiegħek biex tikkonfigura s-settings default. Issa nistgħu nagħmluha!

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

Peress li l-parametri huma mmudellati wara $PSDefaultParameterValues, Tista huwa faċli li tiddiżattivahom temporanjament billi tinstalla ċ-ċavetta "Disabled" fis-sens $true. Benefiċċju addizzjonali ta 'tabella hash separata hija l-abbiltà li tiddiżattiva $WslDefaultParameterValues separatament minn $PSDefaultParameterValues.

Tlestija ta' l-argument

PowerShell jippermettilek tirreġistra trejlers tal-argument billi tuża l-kmand Register-ArgumentCompleter. Bash għandu qawwi għodod programmabbli ta' tlestija awtomatika. WSL jippermettilek li ċċempel bash minn PowerShell. Jekk nistgħu nirreġistraw il-kompletazzjonijiet tal-argument għall-wrappers tal-funzjoni PowerShell tagħna u nsejħu bash biex niġġeneraw it-tlestijiet, niksbu tlestija sħiħa tal-argument bl-istess preċiżjoni bħal bash innifsu!

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

Il-kodiċi huwa daqsxejn dens mingħajr ma nifhmu xi wħud mill-funzjonijiet interni ta 'bash, iżda bażikament dak li nagħmlu huwa dan:

  • Ir-reġistrazzjoni ta' argument kompletar għat-tgeżwir tal-funzjonijiet kollha tagħna billi tgħaddi lista $commands fil-parametru -CommandName għall- Register-ArgumentCompleter.
  • Aħna nimmappjaw kull kmand għall-funzjoni tal-qoxra li bash juża għall-awtokompletazzjoni (biex tiddefinixxi l-ispeċifikazzjonijiet tal-awtokompletazzjoni, bash juża $F, abbrevjazzjoni għal complete -F <FUNCTION>).
  • Konverżjoni ta' Argumenti PowerShell $wordToComplete, $commandAst и $cursorPosition fil-format mistenni mill-funzjonijiet ta 'awtocompletion ta' bash skond l-ispeċifikazzjonijiet awto-kompletazzjoni programmabbli bash.
  • Aħna nikkomponu linja ta 'kmand biex tittrasferixxi għaliha wsl.exe, li jiżgura li l-ambjent huwa stabbilit b'mod korrett, isejjaħ il-funzjoni xierqa ta 'awto-tlestija, u joħroġ ir-riżultati b'mod linja b'linja.
  • Imbagħad insejħu wsl mal-linja tal-kmand, aħna nisseparaw l-output minn separaturi tal-linja u niġġeneraw għal kull wieħed CompletionResults, issortjarhom u jaħarbu karattri bħal spazji u parentesi li inkella jiġu interpretati ħażin.

Bħala riżultat, il-qxur tal-kmand Linux tagħna se jużaw eżattament l-istess awtokompletazzjoni bħal bash! Pereżempju:

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

Kull tlestija awtomatika tipprovdi valuri speċifiċi għall-argument preċedenti, jaqra dejta tal-konfigurazzjoni bħal hosts magħrufa minn WSL!

<TAB> se ċiklu permezz tal-parametri. <Ctrl + пробел> se juri l-għażliet kollha disponibbli.

Barra minn hekk, peress li issa għandna l-awtokompletazzjoni tal-bash, tista 'tikkompleta awtomatikament il-mogħdijiet tal-Linux direttament f'PowerShell!

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

F'każijiet fejn l-awtokompletazzjoni bash ma tipproduċi l-ebda riżultat, PowerShell jerġa' lura għall-mogħdijiet default tas-sistema tal-Windows. Għalhekk, fil-prattika, tista 'fl-istess ħin tuża ż-żewġ mogħdijiet fid-diskrezzjoni tiegħek.

Konklużjoni

Billi nużaw PowerShell u WSL, nistgħu nintegraw il-kmandi tal-Linux fil-Windows bħala applikazzjonijiet indiġeni. M'hemmx bżonn li tfittex Win32 builds jew utilitajiet Linux jew tinterrompi l-fluss tax-xogħol tiegħek billi tmur għal Linux shell. Biss installa WSL, kkonfigurat Profil PowerShell и elenka l-kmandi li trid timporta! L-awtokompletazzjoni rikka għal parametri tal-kmand tal-Linux u tal-Windows u l-mogħdijiet tal-fajls hija funzjonalità li lanqas hija disponibbli fil-kmandi indiġeni tal-Windows illum.

Il-kodiċi tas-sors sħiħ deskritt hawn fuq, kif ukoll linji gwida addizzjonali biex jiġi inkorporat fil-fluss tax-xogħol tiegħek, huma disponibbli hawn.

Liema kmandi tal-Linux issib l-aktar utli? Liema affarijiet komuni oħra huma nieqsa meta taħdem fil-Windows? Ikteb fil-kummenti jew fuq GitHub!

Sors: www.habr.com

Żid kumment