Windowsi arendajate tüüpiline küsimus: „Miks siin ikka ei ole <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?». Будь то мощное пролистывание less või tuttavad tööriistad grep või sed, Windowsi arendajad soovivad nendele käskudele igapäevases töös lihtsalt juurde pääseda.
on selles osas teinud tohutu sammu edasi. See võimaldab käivitada Linuxi käske Windowsist, suunates need läbi wsl.exe (näiteks, wsl ls). Kuigi see on märkimisväärne täiustus, on see lahendus mitmetest puudustest mõjutatud.
- WSLi ulatuslik lisamine
wslon kurnav ja kunstlik. - Windowsi teed argumentides ei tööta alati, kuna tagasiviikude tõlgendatakse kui escape-märke, mitte katalooge eraldavaid märke.
- Windowsi teed argumentides ei muutu vastavaks mounterite punktiks WSL-is.
- WSL profiilides ei arvestata vaikeparameetreid aliase ja keskkonnamuutujatega.
- Linuxi teede lõpetamine ei ole toetatud.
- Käskude lõpetamine ei ole toetatud.
- Argumentide lõpetamine ei ole toetatud.
Linuxi käsud on Windowsi all nagu teise klassi kodanikud — neid on keerulisem kasutada kui kohalikke käske. Nende õiguste tasandamiseks tuleb lahendada loetletud probleemid.
PowerShelli funktsiooni kestad
PowerShelli funktsiooni kestade abil saame lisada käskude automaatse täitmise ja kaotada vajaduse eesliidete järele wsl, tõlkides Windowsi teed WSL-i teedeks. Kestade põhivajadused:
- Iga Linuxi käsu jaoks peab olema üks kest funktsiooni sama nimega.
- Kest peab tuvastama Windowsi teed, mis antakse argumendina, ja konverteerima need WSL-i teedeks.
- Kest peab kutsuma
wslvastava Linuxi käsu mis tahes toru sisendi peale ja edastama kõik argumendid, mis antakse funktsioonile.
Kuna see mall võib kehtida igale käsule, saame nende kestade määratlemise abstraheerida ja genereerida neid dünaamiliselt imporditavate käskude loendist.
# 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 ' ')
}
}
"@
} Loend $command määratleb imporditavad käsud. Seejärel genereerime dünaamiliselt igaühe jaoks funktsiooni ümbermõtestamise, kasutades käsku Invoke-Expression (kui enne eemaldatakse kõik aliasid, mis võivad funktsiooniga vastuollu minna).
Funktsioon käib argumente käsurealt läbi, määrab Windowsi teed käskude abil Split-Path ja Test-Path, ja seejärel muudab need teed WSL-i teedeks. Käivitame teed abifunktsiooni kaudu Format-WslArgument, mille määrame hiljem. See eraldab spetsiaalsed sümbolid, nagu tühikud ja sulud, mis muidu tõlgendatakse valesti.
Lõpuks edastame wsl toru sisendi ja kõik käsurea argumendid.
Selliste ümbermoodustustega on võimalik kutsuda esile oma lemmik Linuxi käsud looduslikumal viisil, ilma et peaksite lisama eesliidet wsl ja muretsema selle üle, kuidas teed muudetakse:
man bashless -i $profile.CurrentUserAllHostsls -Al C:Windows | lessgrep -Ein error *.logtail -f *.log
Siin on esitatud põhikomandide komplekt, kuid saate luua ümber Linuxi käsu, lisades selle lihtsalt nimekirja. Kui lisate selle koodi oma PowerShelli, on need käsud teie jaoks igas PowerShelli seansis saadaval, nagu ka natiivsed käsud!
Vaikimisi seaded
Linuxis on tavaline määrata alias või keskkonnamuutujad profiilides (logi profiil), määrates vaikeseaded sageli kasutatavatele käskudele (näiteks alias ls=ls -AFh või export LESS=-i). Üks puudusi, mis on seotud mitteinteraktiivse shelliga, wsl.exe on see, et profiilid ei lae, seetõttu on need suvandid vaikimisi puuduvad (st, ls WSL-is ja wsl ls käituvad nad erinevalt eelmises määratud aliasest).
PowerShell pakub , standardset mehhanismi vaikeparameetrite määratlemiseks, kuid ainult cmdlet'ide ja arenenud funktsioonide jaoks. Loomulikult saame muuta meie shellid arenenud funktsioonideks, kuid see toob kaasa liigseid keerukusi (nii et PowerShell seob osalised parameetrite nimed (nt, -a seondub -ArgumentList), mis võivad konkureerida Linuxi käskudega, mis aktsepteerivad osalisi nimesid argumentidena), ning vaikeväärtuste määramise süntaks pole kõige sobivam (vaikeargumentide määramiseks on vajalik võtme parameetri nimi, mitte ainult käsu nimi).
Kuid väikese muudatusega meie shellides saame rakendada sarnase mudeli $PSDefaultParameterValues, ja lisada vaikimisi parameetrid Linuxi käskudele!
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 ' ')
}
} Edastades $WslDefaultParameterValues komandoreas, saadame parameetrid läbi wsl.exe. Allpool on näidatud, kuidas lisada juhiseid PowerShelli profiili vaikeseadete konfigureerimiseks. Nüüd saame seda teha!
$WslDefaultParameterValues["grep"] = "-E"
$WslDefaultParameterValues["less"] = "-i"
$WslDefaultParameterValues["ls"] = "-AFh --group-directories-first" Kuna parameetrid modelleeritakse pärast $PSDefaultParameterValues, saate ajaliselt, seades võtme "Disabled" väärtuse $true. Eraldi hash-tabeli täiendav eelis on võimalus väljalülitada $WslDefaultParameterValues eriti $PSDefaultParameterValues.
Argumentide automaatne täiendamine
PowerShell võimaldab registreerida argumentide täiendajad käsu kaudu Register-ArgumentCompleter. Bashil on võimsad . WSL võimaldab bash'i kutsuda PowerShellist. Kui me saame registreerida argumentide lõpetajad meie PowerShelli funktsioonide jaoks ja kutsuda bash'i lõpetuste genereerimiseks, siis saame täieliku argumentide automaatse lõpetamise sama täpsusega nagu bash'is!
# 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]
}
}Kood on veidi tihe, kui mitte mõista mõningaid bash'i sisemisi funktsioone, kuid enamasti teeme järgmist:
- Registreerime argumentide lõpetajaid kõikide meie funktsioonide ümber, edastades nimekirja
$commandsparameetrisse-CommandNamekunaRegister-ArgumentCompleter. - Seome iga käskluse bash'i kasutatava shell'i funktsiooniga automaatseks lõpetamiseks (bash'is automaatse lõpetamise spetsifikatsioonide määratlemiseks kasutatakse
$F, lühendcomplete -F). - Muutame PowerShelli argumente
$wordToComplete,$commandAstja$cursorPositionformaadiks, mida bash'i automaatse lõpetamise funktsioonid ootavad vastavalt bash. - Koostame käsurea edastamiseks
wsl.exe, mis tagab keskkonna õige seadistamise, kutsub esile vastava automaatse lõpetamise funktsiooni ja tagastab tulemused ridu eraldatuna. - Seejärel kutsume
wslkoos käsureaga, jagame väljundi ridadega ja genereerime igaühe jaoksCompletionResults, sorteerides need ja escaping märke, nagu tühikud ja sulud, mis muidu tõlgendataks valesti.
Lõpuks kasutavad meie Linuxi käsuread täpselt sellist automaatset täiendamist nagu bash! Näiteks:
ssh -c -J -m -O -o -Q -w -b
Iga automaatne täiendamine pakub väärtusi, mis on spetsiifilised eelnevale argumendile, lugedes seadistuse andmeid, nagu tuntud hostid, WSL-ist!
<TAB> korrutab parameetrid läbi. <Ctrl + пробел> näitab kõiki saadaolevaid valikuid.
Lisaks, kuna meil on nüüd töötav bash automaatne täiendamine, saate automaatse täiendamine Linuxi teedele otse PowerShellis!
less /etc/ls /usr/share/vim ~/bash
Situatsioonides, kus bash automaatne täiendamine ei anna tulemusi, naaseb PowerShell vaikimisi Windowsi teede süsteemi juurde. Seega saate praktikas kasutada mõlemat teed vastavalt oma soovile.
Kokkuvõte
PowerShelli ja WSLi abil saame integreerida Linuxi käsud Windowsi kui natiivsed rakendused. Pole vaja otsida Win32 versioone või Linuxi tööriistu ega katkestada tööd, minnes Linuxi konsooli. Lihtsalt , seadistage ja ! Rikkalik automaatne täitmine Linuxi ja Windowsi failiteede käsu valikute jaoks on funktsionaalsus, mida isegi Windowsi natiivsetes käskudes täna pole.
Ülaltoodud täielik allikas, samuti täiendavad soovitused selle integreerimiseks töövoogu on saadaval .
Milliseid Linuxi käske peate kõige kasulikumaks? Millest veel tuttavast asjadest on puudu Windowsis töötades? Kirjutage kommentaarides või !
Allikas: habr.com
