Typická otázka od vývojárov Windows„Prečo tu ešte nič nie je?“ <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?. Či už ide o silný ťah less alebo známe nástroje grep alebo sed, vývojári pod Windows chcú mať k týmto príkazom jednoduchý prístup pri svojej každodennej práci.
v tomto smere urobil obrovský krok vpred. Umožňuje vám volať príkazy Linux z Windows, sprostredkovaním ich prostredníctvom wsl.exe (Napr wsl ls). Hoci ide o výrazné zlepšenie, táto možnosť trpí množstvom nevýhod.
- Všadeprítomný prídavok
wslúnavné a neprirodzené. - Cesty Windows v argumentoch nie vždy fungujú, pretože spätné lomky sa interpretujú ako escape znaky, nie ako oddeľovače adresárov.
- Cesty Windows Argumenty v WSL sa neprekladajú do zodpovedajúceho bodu pripojenia.
- V profiloch WSL s aliasmi a premennými prostredia sa nerešpektujú predvolené nastavenia.
- Dokončovanie cesty nie je podporované Linux.
- Dokončenie príkazu nie je podporované.
- Dokončenie argumentu nie je podporované.
V dôsledku tímu Linux sú vnímané pod Windows Sú ako občania druhej kategórie – a je ťažšie ich využiť ako ich domáce tímy. Aby sa im vyrovnali práva, je potrebné tieto problémy riešiť.
Obal funkcií PowerShell
Pomocou obalov funkcií PowerShell môžeme pridať dokončenie príkazov a eliminovať potrebu prefixov wsl, vysielacie cesty Windows Na ceste WSL. Základné požiadavky na shell:
- Pre každý tím Linux Musí existovať jeden funkčný obal s rovnakým názvom.
- Shell musí rozpoznávať cesty Windows, odovzdané ako argumenty a previesť ich na cesty WSL.
- Škrupina by mala zavolať
wsls príslušným príkazom Linux na akýkoľvek vstup kanála a odovzdávanie akýchkoľvek argumentov príkazového riadka odovzdaných funkcii.
Keďže tento vzor možno použiť na akýkoľvek príkaz, môžeme abstrahovať definíciu týchto obalov a dynamicky ich generovať zo zoznamu príkazov na import.
# 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 ' ')
}
}
"@
} Zoznam $command definuje importné príkazy. Potom pomocou príkazu dynamicky vygenerujeme obal funkcií pre každý z nich Invoke-Expression (najprv odstránením všetkých aliasov, ktoré by boli v konflikte s funkciou).
Funkcia iteruje cez argumenty príkazového riadku a určuje cesty Windows pomocou príkazov Split-Path и Test-Patha potom tieto cesty skonvertuje na cesty WSL. Cesty vedieme cez pomocnú funkciu Format-WslArgument, ktorý si zadefinujeme neskôr. Vynecháva špeciálne znaky, ako sú medzery a zátvorky, ktoré by inak boli nesprávne interpretované.
Nakoniec prenesieme wsl potrubný vstup a všetky argumenty príkazového riadku.
Pomocou takýchto obalov môžete volať svoje obľúbené príkazy Linux prirodzenejším spôsobom, bez pridania predpony wsl a bez obáv o to, ako sa cesty prevedú:
man bashless -i $profile.CurrentUserAllHostsls -Al C:Windows | lessgrep -Ein error *.logtail -f *.log
Tu je zobrazená základná sada príkazov, ale pre ľubovoľný príkaz môžete vytvoriť obal. Linux, jednoduchým pridaním do zoznamu. Ak pridáte tento kód do svojho PowerShell, tieto príkazy budete mať k dispozícii v každej relácii PowerShell, rovnako ako natívne príkazy!
Predvolené nastavenia
В Linux Je bežné definovať aliasy a/alebo premenné prostredia v profiloch (prihlasovací profil), čím sa nastavia predvolené parametre pre často používané príkazy (napríklad alias ls=ls -AFh alebo export LESS=-i). Jedna z nevýhod proxyovania cez neinteraktívny shell wsl.exe - že profily nie sú načítané, takže tieto možnosti nie sú štandardne dostupné (t.j. ls vo WSL a wsl ls sa bude správať inak s aliasom definovaným vyššie).
PowerShell poskytuje , štandardný mechanizmus na definovanie predvolených parametrov, ale len pre cmdlety a pokročilé funkcie. Samozrejme, môžeme z našich shellov urobiť pokročilé funkcie, ale to prináša zbytočné komplikácie (napríklad PowerShell koreluje názvy čiastkových parametrov (napr. -a koreluje s -ArgumentList), čo bude v konflikte s príkazmi Linux, pričom ako argumenty sa berú čiastočné názvy) a syntax na definovanie predvolených hodnôt nebude najvhodnejšia (definovanie predvolených argumentov vyžaduje názov parametra v prepínači, nielen názov príkazu).
S miernou úpravou našich mušlí však môžeme implementovať model podobný ako $PSDefaultParameterValuesa povoliť predvolené možnosti pre príkazy 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 ' ')
}
} Pasovanie $WslDefaultParameterValues do príkazového riadku posielame parametre cez wsl.exe. Nasledujúci text ukazuje, ako pridať pokyny do profilu PowerShell na konfiguráciu predvolených nastavení. Teraz to dokážeme!
$WslDefaultParameterValues["grep"] = "-E"
$WslDefaultParameterValues["less"] = "-i"
$WslDefaultParameterValues["ls"] = "-AFh --group-directories-first" Keďže parametre sú modelované po $PSDefaultParameterValuesmôžete dočasne inštaláciou kľúča "Disabled" do významu $true. Ďalšou výhodou samostatnej hašovacej tabuľky je možnosť deaktivácie $WslDefaultParameterValues oddelene od $PSDefaultParameterValues.
Dokončenie argumentácie
PowerShell vám umožňuje zaregistrovať upútavky argumentov pomocou príkazu Register-ArgumentCompleter. Bash má moc . WSL vám umožňuje volať bash z PowerShell. Ak dokážeme zaregistrovať dokončenia argumentov pre naše obaly funkcií PowerShell a zavolať bash na generovanie dokončení, získame úplné dokončenie argumentov s rovnakou presnosťou ako samotný 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]
}
}Kód je trochu hustý bez pochopenia niektorých vnútorných funkcií bashu, ale v podstate robíme toto:
- Registrácia dokončovača argumentov pre všetky naše funkcie wrapper odovzdaním zoznamu
$commandsv parametri-CommandNamepreRegister-ArgumentCompleter. - Každý príkaz mapujeme na funkciu shell, ktorú bash používa na automatické dopĺňanie (na definovanie špecifikácií automatického dopĺňania používa bash
$F, skratka precomplete -F <FUNCTION>). - Konverzia argumentov PowerShell
$wordToComplete,$commandAstи$cursorPositiondo formátu očakávaného funkciami automatického dopĺňania bash podľa špecifikácií bash. - Vytvoríme príkazový riadok na prenos
wsl.exe, ktorý zaisťuje správne nastavenie prostredia, zavolá príslušnú funkciu automatického dokončovania a výsledky vypíše riadok po riadku. - Potom zavoláme
wsls príkazovým riadkom oddeľujeme výstup oddeľovačmi riadkov a generujeme pre každýCompletionResults, ich triedenie a escapovanie znakov, ako sú medzery a zátvorky, ktoré by inak boli nesprávne interpretované.
Výsledkom je, že naše veliteľské shell-y Linux použije presne to isté automatické dopĺňanie ako v bash! Napríklad:
ssh -c <TAB> -J <TAB> -m <TAB> -O <TAB> -o <TAB> -Q <TAB> -w <TAB> -b <TAB>
Každé automatické dopĺňanie dodáva hodnoty špecifické pre predchádzajúci argument, čítajúc konfiguračné údaje, ako sú napríklad známi hostitelia z WSL!
<TAB> bude cyklicky prechádzať parametrami. <Ctrl + пробел> zobrazí všetky dostupné možnosti.
Keďže teraz máme funkčné automatické dopĺňanie v bash, môžete automaticky dopĺňať cesty Linux priamo v PowerShelle!
less /etc/<TAB>ls /usr/share/<TAB>vim ~/.bash<TAB>
V prípadoch, keď automatické dopĺňanie v bash neprináša žiadne výsledky, PowerShell sa vráti k predvoleným systémovým cestám. WindowsV praxi teda môžete podľa vlastného uváženia použiť obe cesty súčasne.
Záver
Pomocou PowerShellu a WSL môžeme integrovať príkazy Linux в Windows ako natívne aplikácie. Nie je potrebné hľadať zostavenia alebo nástroje pre Win32 Linux alebo prerušte pracovný postup prechodom na Linux-shell. Len , nakonfigurovať и Bohaté automatické dopĺňanie pre parametre príkazov a cesty k súborom! Linux и Windows — toto je funkcia, ktorá dnes nie je dostupná ani v natívnych príkazoch Windows.
K dispozícii je úplný zdrojový kód popísaný vyššie, ako aj ďalšie pokyny na jeho začlenenie do vášho pracovného postupu .
Aké tímy? Linux Čo považujete za najužitočnejšie? Aké ďalšie známe veci chýbajú pri práci v WindowsNapíšte do komentárov alebo !
Zdroj: hab.com
