Linux komandu integrÄÅ”ana sistÄmÄ Windows, izmantojot PowerShell un WSL
Tipisks Windows izstrÄdÄtÄju jautÄjums: āKÄpÄc joprojÄm nav <ŠŠ”Š¢ŠŠŠ¬Š¢Š Š¢Š£Š¢ ŠŠ®ŠŠŠŠ£Š® ŠŠŠŠŠŠŠ£ LINUX>?. NeatkarÄ«gi no tÄ, vai tas ir spÄcÄ«gs vilkums less vai pazÄ«stami rÄ«ki grep vai sed, Windows izstrÄdÄtÄji vÄlas vieglu piekļuvi Ŕīm komandÄm ikdienas darbÄ.
Windows apakÅ”sistÄma operÄtÄjsistÄmai Linux (WSL) Å”ajÄ ziÅÄ ir spÄris milzÄ«gu soli uz priekÅ”u. Tas ļauj izsaukt Linux komandas no Windows, izmantojot starpniekserveri wsl.exe (PiemÄram, wsl ls). Lai gan tas ir bÅ«tisks uzlabojums, Å”ai iespÄjai ir vairÄki trÅ«kumi.
VisuresoÅ”s papildinÄjums wsl nogurdinoÅ”i un nedabiski.
Windows ceļi argumentos ne vienmÄr darbojas, jo slÄ«psvÄ«tras tiek interpretÄtas kÄ atsoļa rakstzÄ«mes, nevis direktoriju atdalÄ«tÄji.
Windows ceļi argumentos netiek tulkoti uz atbilstoŔo pievienoŔanas punktu WSL.
WSL profilos ar aizstÄjvÄrdiem un vides mainÄ«gajiem netiek ievÄroti noklusÄjuma iestatÄ«jumi.
Linux ceļa pabeigŔana netiek atbalstīta.
Komandu pabeigŔana netiek atbalstīta.
Argumenta pabeigŔana netiek atbalstīta.
RezultÄtÄ operÄtÄjsistÄmÄ Windows Linux komandas tiek uzskatÄ«tas par otrÄs klases pilsoÅiem, 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 aptinÄji
Izmantojot PowerShell funkciju aptinumus, mÄs varam pievienot komandu pabeigÅ”anu un novÄrst nepiecieÅ”amÄ«bu pÄc prefiksiem wsl, pÄrvÄrÅ”ot Windows ceļus WSL ceļos. PamatprasÄ«bas ÄaulÄm:
Katrai Linux komandai ir jÄbÅ«t vienam funkciju iesaiÅojumam ar tÄdu paÅ”u nosaukumu.
Apvalkam ir jÄatpazÄ«st Windows ceļi, kas nodoti kÄ argumenti, un jÄpÄrvÄrÅ” tie par WSL ceļiem.
Apvalkam vajadzÄtu piezvanÄ«t wsl ar atbilstoÅ”o Linux komandu jebkurai konveijera ievadei un nododot visus funkcijai nodotos komandrindas argumentus.
TÄ kÄ Å”o modeli var lietot jebkurai komandai, mÄs varam abstrahÄt Å”o iesaiÅojumu definÄ«ciju un dinamiski Ä£enerÄt tos no importÄjamo komandu saraksta.
# 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 definÄ importÄÅ”anas komandas. PÄc tam mÄs dinamiski Ä£enerÄjam funkciju iesaiÅojumu katram no tiem, izmantojot komandu Invoke-Expression (vispirms noÅemot visus aizstÄjvÄrdus, kas varÄtu bÅ«t pretrunÄ ar funkciju).
Funkcija atkÄrtojas virs komandrindas argumentiem, nosaka Windows ceļus, izmantojot komandas Split-Path Šø Test-Pathun pÄc tam pÄrvÄrÅ” Å”os ceļus par WSL ceļiem. MÄs veicam ceļus, izmantojot palÄ«gfunkciju Format-WslArgument, ko mÄs definÄsim vÄlÄk. TajÄ netiek izmantotas Ä«paÅ”as rakstzÄ«mes, piemÄram, atstarpes un iekavas, kas citÄdi tiktu nepareizi interpretÄtas.
Visbeidzot, mÄs nododam wsl konveijera ievadi un visus komandrindas argumentus.
Izmantojot Å”os aptinumus, jÅ«s varat izsaukt savas iecienÄ«tÄkÄs Linux komandas dabiskÄkÄ veidÄ, nepievienojot prefiksu wsl un neuztraucoties par to, kÄ ceļi tiek pÄrveidoti:
man bash
less -i $profile.CurrentUserAllHosts
ls -Al C:Windows | less
grep -Ein error *.log
tail -f *.log
Å eit ir parÄdÄ«ta pamata komandu kopa, taÄu jebkurai Linux komandai varat izveidot Äaulu, vienkÄrÅ”i pievienojot to sarakstam. Ja pievienosit Å”o kodu savam profils PowerShell, Ŕīs komandas jums bÅ«s pieejamas katrÄ PowerShell sesijÄ, tÄpat kÄ vietÄjÄs komandas!
NoklusÄjuma iestatÄ«jumi
OperÄtÄjsistÄmÄ Linux pieteikÅ”anÄs profilos parasti tiek definÄti aizstÄjvÄrdi un/vai vides mainÄ«gie, iestatot noklusÄjuma parametrus bieži izmantotajÄm komandÄm (piemÄram, alias ls=ls -AFh vai export LESS=-i). Viens no trÅ«kumiem starpniekserverÄ«, izmantojot neinteraktÄ«vu apvalku wsl.exe - ka profili nav ielÄdÄti, tÄpÄc Ŕīs opcijas pÄc noklusÄjuma nav pieejamas (t.i. ls WSL un wsl ls darbosies atŔķirÄ«gi ar iepriekÅ” definÄto aizstÄjvÄrdu).
PowerShell nodroÅ”ina $PSDefaultParameterValues, standarta mehÄnisms noklusÄjuma parametru definÄÅ”anai, bet tikai cmdlet un papildu funkcijÄm. Protams, mÄs varam izveidot uzlabotas funkcijas no mÅ«su ÄaulÄm, taÄu tas rada nevajadzÄ«gus sarežģījumus (piemÄram, PowerShell korelÄ daļÄjus parametru nosaukumus (piemÄram, -a korelÄ ar -ArgumentList), kas bÅ«s pretrunÄ ar Linux komandÄm, kas izmanto daļÄjus nosaukumus kÄ argumentus), un noklusÄjuma vÄrtÄ«bu definÄÅ”anas sintakse nebÅ«s vispiemÄrotÄkÄ (noklusÄjuma argumentiem ir nepiecieÅ”ams parametra nosaukums atslÄgÄ, nevis tikai komandas nosaukums) .
TomÄr, nedaudz pÄrveidojot mÅ«su apvalkus, mÄs varam ieviest modeli, kas ir lÄ«dzÄ«gs $PSDefaultParameterValuesun iespÄjojiet noklusÄjuma opcijas Linux komandÄm!
Ejot garÄm $WslDefaultParameterValues uz komandrindu, mÄs nosÅ«tÄm parametrus, izmantojot wsl.exe. TÄlÄk ir parÄdÄ«ts, kÄ PowerShell profilam pievienot norÄdÄ«jumus, lai konfigurÄtu noklusÄjuma iestatÄ«jumus. Tagad mÄs to varam!
TÄ kÄ parametri ir modelÄti pÄc $PSDefaultParameterValuesjÅ«s varat tos ir viegli atspÄjot Ä«slaicÄ«gi, uzstÄdot atslÄgu "Disabled" nozÄ«mÄ $true. AtseviŔķas hash tabulas papildu priekÅ”rocÄ«ba ir iespÄja atspÄjot $WslDefaultParameterValues atseviŔķi no $PSDefaultParameterValues.
Argumenta pabeigŔana
PowerShell ļauj reÄ£istrÄt argumentu piekabes, izmantojot komandu Register-ArgumentCompleter. BaÅ”am ir spÄcÄ«gs programmÄjami automÄtiskÄs pabeigÅ”anas rÄ«ki. WSL ļauj izsaukt bash no PowerShell. Ja mÄs varam reÄ£istrÄt argumentu pabeigÅ”anas mÅ«su PowerShell funkciju iesaiÅojumos un izsaukt bash, lai Ä£enerÄtu pabeigÅ”anas, mÄs iegÅ«stam pilnÄ«gu argumentu pabeigÅ”anu ar tÄdu paÅ”u precizitÄti kÄ pats 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, neizprotot dažas bash iekÅ”ÄjÄs funkcijas, bet pamatÄ tas, ko mÄs darÄm, ir Å”Äds:
Argumentu pabeigtÄja reÄ£istrÄÅ”ana visiem mÅ«su funkciju iesaiÅojumiem, nododot sarakstu $commands parametrÄ -CommandName par Register-ArgumentCompleter.
Katra komanda tiek kartÄta uz Äaulas funkciju, ko bash izmanto automÄtiskajai pabeigÅ”anai (lai noteiktu automÄtiskÄs pabeigÅ”anas specifikÄcijas, bash izmanto $F, saÄ«sinÄjums vÄrdam complete -F <FUNCTION>).
PowerShell argumentu konvertÄÅ”ana $wordToComplete, $commandAst Šø $cursorPosition formÄtÄ, ko paredz bash automÄtiskÄs pabeigÅ”anas funkcijas saskaÅÄ ar specifikÄcijÄm programmÄjama automÄtiskÄ pabeigÅ”ana bash.
MÄs izveidojam komandrindu, uz kuru pÄrsÅ«tÄ«t wsl.exe, kas nodroÅ”ina, ka vide ir iestatÄ«ta pareizi, izsauc atbilstoÅ”o automÄtiskÄs pabeigÅ”anas funkciju un izvada rezultÄtus pa rindiÅai.
Tad zvanÄm wsl ar komandrindu mÄs atdalÄm izvadi ar rindu atdalÄ«tÄjiem un Ä£enerÄjam katram CompletionResults, kÄrtojot tos un izlaižot rakstzÄ«mes, piemÄram, atstarpes un iekavas, kas citÄdi tiktu nepareizi interpretÄtas.
RezultÄtÄ mÅ«su Linux komandu Äaulas izmantos tieÅ”i tÄdu paÅ”u automÄtisko pabeigÅ”anu kÄ bash! PiemÄram:
Katra automÄtiskÄ pabeigÅ”ana nodroÅ”ina vÄrtÄ«bas, kas raksturÄ«gas iepriekÅ”Äjam argumentam, nolasot konfigurÄcijas datus, piemÄram, zinÄmos saimniekdatorus no WSL!
<TAB> ciklos pa parametriem. <Ctrl + ŠæŃŠ¾Š±ŠµŠ»> parÄdÄ«s visas pieejamÄs opcijas.
TurklÄt, tÄ kÄ mums tagad ir bash automÄtiskÄ pabeigÅ”ana, varat automÄtiski pabeigt Linux ceļus tieÅ”i programmÄ PowerShell!
less /etc/<TAB>
ls /usr/share/<TAB>
vim ~/.bash<TAB>
GadÄ«jumos, kad bash automÄtiskÄ pabeigÅ”ana nesniedz nekÄdus rezultÄtus, PowerShell atgriežas pie sistÄmas noklusÄjuma Windows ceļiem. TÄdÄjÄdi praksÄ jÅ«s varat vienlaikus izmantot abus ceļus pÄc saviem ieskatiem.
SecinÄjums
Izmantojot PowerShell un WSL, mÄs varam integrÄt Linux komandas sistÄmÄ Windows kÄ vietÄjÄs lietojumprogrammas. Nav nepiecieÅ”ams meklÄt Win32 bÅ«vÄjumus vai Linux utilÄ«tas vai pÄrtraukt darbplÅ«smu, atverot Linux Äaulu. VienkÄrÅ”i instalÄt WSL, konfigurÄt PowerShell profils Šø uzskaitiet komandas, kuras vÄlaties importÄt! BagÄtÄ«ga automÄtiskÄ pabeigÅ”ana Linux un Windows komandu parametriem un failu ceļiem ir funkcionalitÄte, kas mÅ«sdienÄs nav pieejama pat vietÄjÄs Windows komandÄs.
Ir pieejams pilns iepriekÅ” aprakstÄ«tais pirmkods, kÄ arÄ« papildu vadlÄ«nijas tÄ iekļauÅ”anai darbplÅ«smÄ Å”eit.
Kuras Linux komandas jums Ŕķiet visnoderÄ«gÄkÄs? KÄdas citas bieži sastopamas lietas trÅ«kst, strÄdÄjot sistÄmÄ Windows? Raksti komentÄros vai vietnÄ GitHub!