Integrējam Linux komandas Windows vidē ar PowerShell un WSL

Tipiska programma izstrādātājiem Windows: ā€žKāpēc Å”eit joprojām nav <ВДТАВЬТЕ ТУТ Š›Š®Š‘Š˜ŠœŠ£Š® ŠšŠžŠœŠŠŠ”Š£ LINUX>?Ā». Š‘ŃƒŠ“ŃŒ то мощное пролистывание less vai ierastie rÄ«ki grep vai sed, izstrādātāji Windows vēlas viegli piekļūt Ŕīm komandām ikdienas darbā.

Windows subsistēma Linux (WSL) ir bÅ«tiski uzlabojusi Å”o situāciju. Tā ļauj izsaukt Linux komandas no Windows, tās proksējot caur wsl.exe (piemēram, wsl ls). Lai gan tas ir bÅ«tisks uzlabojums, Ŕāds risinājums cieÅ” no vairākiem trÅ«kumiem.

  • Vispārēja pievienoÅ”ana wsl ir nogurdinoÅ”a un dabiski nejauka.
  • Windows ceļi argumentu sarakstā ne vienmēr strādā, jo atpakaļ slÄ«psvÄ«tras tiek interpretētas kā escape simboli, nevis katalogu dalÄ«tāji.
  • Windows ceļi argumentu sarakstā netiek pārvērsti atbilstoÅ”ajā WSL piekļuves punktā.
  • Nekādas noklusējuma opcijas WSL profilos ar aliasiem un vides mainÄ«gajiem netiek ņemtas vērā.
  • Linux ceļu pabeigÅ”ana netiek atbalstÄ«ta.
  • Komandu pabeigÅ”ana netiek atbalstÄ«ta.
  • Argumentu pabeigÅ”ana netiek atbalstÄ«ta.

Tādējādi Linux komandas Windows sistēmā tiek uztvertas kā otro Ŕķiru pilsoņi — un tās ir grÅ«tāk izmantot nekā vietējās komandas. Lai izlÄ«dzinātu viņu tiesÄ«bas, ir jāatrisina uzskaitÄ«tās problēmas.

PowerShell funkciju apvalki

Ar PowerShell funkciju apvalku palÄ«dzÄ«bu mēs varam pievienot komandu automātisku pabeigÅ”anu un novērst priekÅ”nosacÄ«jumu nepiecieÅ”amÄ«bu, wsl, tulkojot Windows ceļus WSL ceļos. Galvenās prasÄ«bas apvalkiem ir:

  • Katram Linux komandam jābÅ«t vienam funkciju apvalkam ar to paÅ”u nosaukumu.
  • Apvalkam jāatzÄ«st Windows ceļi, kas nodoti kā argumenti, un jāpārveido tie par WSL ceļiem.
  • Apvalkam jāizsauc wsl ar attiecÄ«gu Linux komandu par jebkuru caurlaides ievadi un jāpārsÅ«ta jebkuri komandrindas argumenti, kas nodoti funkcijai.

Kā Å”is Å”ablons var tikt piemērots jebkurai komandai, mēs varam abstrahēt Å”o apvalku definÄ«ciju un dinamiski Ä£enerēt tos no komandu saraksta importam.

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

Saraksts $command nosaka komandas importēŔanai. Pēc tam mēs dinamiski Ä£enerējam funkciju apvalku katrai no tām, izmantojot komandu Invoke-Expression (sākumā izņemot jebkurus aliasus, kas varētu konfliktēt ar funkciju).

Funkcija pārlÅ«ko komandrindas argumentus, nosaka Windows ceļus, izmantojot komandas Split-Path un Test-Path, un pēc tam tos pārvērÅ” WSL ceļos. Mēs palaidām ceļus caur palÄ«gfunkciju Format-WslArgument, kuru definēsim vēlāk. Tā aizsargā speciālos simbolus, piemēram, atstarpes un iekavas, kas citādi tiktu nepareizi interpretēti.

Visbeidzot, nododam wsl ievades cauruli un jebkurus komandrindas argumentus.

Izmantojot Ŕādus ietvarus, varat izsaukt savas iecienÄ«tākās Linux komandas dabiski, nepievienojot prefiksu wsl un neuztraucoties, kā ceļi tiks pārvērsti:

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

Å eit ir parasts komandu kopums, bet jÅ«s varat izveidot apvalku jebkurai Linux komandai, vienkārÅ”i pievienojot to sarakstam. Ja jÅ«s pievienosiet Å”o kodu savam profilam PowerShell, Ŕīs komandas bÅ«s pieejamas katrā PowerShell sesijā, tāpat kā vietējās komandas!

Noklusējuma opcijas

Linux ir pierasts definēt aliasus un/vai vides mainÄ«gos profilos (login profile), nosakot noklusējuma parametrus bieži lietotām komandām (piemēram, alias ls=ls -AFh vai export LESS=-i). Viens no trÅ«kumiem, izmantojot neinteraktÄ«vu apvalku wsl.exe ir tas, ka profili netiek ielādēti, tāpēc Å”ie noklusējuma parametri nav pieejami (t.i., ls WSL un wsl ls uzvedÄ«sies savādāk ar iepriekÅ” noteikto aliasu).

PowerShell nodroÅ”ina $PSDefaultParameterValues, standarta mehānisms, lai noteiktu noklusējuma parametrus, bet tikai komandletiem un paplaÅ”inātām funkcijām. Protams, varam no mÅ«su apvalkiem izveidot paplaÅ”inātas funkcijas, bet tas radÄ«s liekas sarežģījumus (tādējādi PowerShell sasaistÄ«s daļējas parametru nosaukumu (piemēram, -a sasaistās ar -ArgumentList), kas radÄ«s konfliktus ar Linux komandām, kas pieņem daļējus nosaukumus kā argumentus), un sintakse noklusējuma vērtÄ«bu noteikÅ”anai nebÅ«s vispiemērotākā (lai noteiktu noklusējuma argumentus, parametra nosaukumam jābÅ«t atslēgā, nevis tikai komandas nosaukumam).

Tomēr ar nelielu mūsu apvalku izmaiņu mēs varam ieviest modeli, kas ir līdzīgs $PSDefaultParameterValues, un iekļaut noklusējuma parametrus Linux komandām!

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 ' ')
    }
}

Pārsūtot $WslDefaultParameterValues komandrā mēs nosūtām parametrus caur wsl.exe. Zemāk norādīts, kā pievienot instrukcijas PowerShell profilam, lai iestatītu noklusējuma parametrus. Tagad mēs to varam izdarīt!

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

Jo parametri tiek modelēti pēc $PSDefaultParameterValues, jÅ«s varat viegli tos atspējot uz laiku, iestatot atslēgu "Disabled" uz vērtÄ«bu $true. Papildu priekÅ”rocÄ«ba atseviŔķai Hash tabulai ir iespēja atspējot $WslDefaultParameterValues atseviŔķi no $PSDefaultParameterValues.

Automātiskā pabeigŔana argumentiem

PowerShell ļauj reÄ£istrēt argumentu pabeidotājus, izmantojot komandu Register-ArgumentCompleter. Bash ir jaudÄ«gas programmējamas automātiskās pabeigÅ”anas iespējas. WSL ļauj izsaukt bash no PowerShell. Ja mēs varam reÄ£istrēt argumentu pabeidotājus savām funkciju konsolēm PowerShell un izsaukt bash, lai izveidotu pabeigÅ”anas, tad mēs iegÅ«sim pilnÄ«gu argumentu automātisko pabeigÅ”anu ar tādu paÅ”u precizitāti kā paŔā bash!

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

Kods ir nedaudz blÄ«vs bez izpratnes par dažām iekŔējām bash funkcijām, bet galvenokārt mēs darām sekojoÅ”o:

  • ReÄ£istrējam argumentu pabeidotāju visām mÅ«su funkciju apvalku, nododot sarakstu $commands parametrā -CommandName lai Register-ArgumentCompleter.
  • Saskaņojam katru komandu ar funkciju apvalku, ko bash izmanto automātiskai pabeigÅ”anai (specifikāciju noteikÅ”anai automātiskai pabeigÅ”anai bash tiek izmantots $F, saÄ«sinājums no complete -F).
  • PārvērÅ”am PowerShell argumentus $wordToComplete, $commandAst un $cursorPosition formātā, ko sagaida bash automātiskās pabeigÅ”anas funkcijas saskaņā ar programmējamo automātisko pabeigÅ”anu bash.
  • Sastādām komandrindas komandu, lai nodotu wsl.exe, kas nodroÅ”ina pareizu vides iestatÄ«jumu, izsauc attiecÄ«go automātiskās pabeigÅ”anas funkciju un izvada rezultātus ar lÄ«niju sadalÄ«jumu.
  • Tad izsaucam wsl ar komandrindu, sadalām rezultātu pa rindām un Ä£enerējam katram CompletionResults, sakārtojot tos un izvairoties no simboliem, piemēram, atstarpēm un iekavām, kas citādi tiktu nepareizi interpretēti.

Galu galā mÅ«su Linux komandrindas apvalki izmantos tieÅ”i tādu paÅ”u automātisko pabeigÅ”anu kā bash! Piemēram:

  • ssh -c -J -m -O -o -Q -w -b

Katram automātiskajam pabeigÅ”anai ir pieejami iepriekŔējā argumenta specifiski vērtÄ«bas, nolasot konfigurācijas datus, piemēram, zināmās viesu resursus no WSL!

<TAB> cikliski pārskatÄ«s parametrus. <Ctrl + пробел> parādÄ«s visus pieejamos variantus.

Turklāt, tagad, kad mums darbojas bash automātiskā pabeigŔana, jūs varat automātiski pabeigt Linux ceļus tieŔi PowerShell!

  • less /etc/
  • ls /usr/share/
  • vim ~/\.bash

Ja gadÄ«jumos, kad bash automātiskā pabeigÅ”ana nenodroÅ”ina rezultātus, PowerShell atgriežas pie noklusējuma sistēmas ar Windows ceļiem. Tādējādi jÅ«s varat praktiski vienlaikus izmantot abus ceļus pēc jÅ«su izvēles.

Secinājums

Ar PowerShell un WSL mēs varam integrēt Linux komandas Windows kā vietējās lietojumprogrammas. Nav nepiecieÅ”ams meklēt Win32 versijas vai Linux utilÄ«tus vai pārtraukt darba plÅ«smu, pārejot uz Linux komandu rindu. VienkārÅ”i uzstādiet WSL, konfigurējiet PowerShell profilu un uzskaitiet komandas, kuras vēlaties importēt! BagātÄ«ga automātiskā pabeigÅ”ana komandparametriem un ceļiem uz failiem Linux un Windows — Ŕī ir funkcionalitāte, kāda Å”odien pat nav vietējās Windows komandās.

Pilnais izstrādātais kods, kas aprakstÄ«ts iepriekÅ”, kā arÄ« papildu ieteikumi tā integrēŔanai darba plÅ«smā ir pieejami. Å”eit.

Kuras Linux komandas jūs uzskatāt par visnoderīgākajām? Kādas citas ierastas lietas trūkst, strādājot Windows? Rakstiet komentāros vai GitHub!

Avots: habr.com

Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem šŸ”„ Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem | ProHoster