Një pyetje e zakonshme për zhvilluesit në Windows është: «Pse këtu ende nuk ka <QI KETU KOMANDEN TUAJ PËR LINUX>?». Будь то мощное пролистывание më pak apo mjetet e njohura grep ose sed, zhvilluesit në Windows dëshirojnë qasje të lehtë në këto komanda në punën e përditshme.
ka bërë një hap të madh përpara në këtë drejtim. Ai lejon thirrjen e komandave Linux nga Windows, duke i kaluar ato përmes wsl.exe (p.sh., wsl ls). Megjithëse kjo është një përmirësim i rëndësishëm, kjo qasje ka disa mangësi.
- Shtimi i gjithanshëm të
wslështë lodhëse dhe e natyrshme. - Rugët e Windows në argumente nuk funksionojnë gjithmonë, sepse slashes e prapme interpretohen si simbole escape dhe jo si ndarës katalogësh.
- Rugët e Windows në argumente nuk shndërrohen në pikën përkatëse të montimit në WSL.
- Parametrat e paracaktuar në profilin WSL me alias dhe variabla mjedisi nuk merret parasysh.
- Përfundimet e rrugëve Linux nuk mbështeten.
- Përfundimet e komandave nuk mbështeten.
- Përfundimet e argumenteve nuk mbështeten.
Si rezultat, komandat Linux perceptohen nga Windows si qytetarë të dorës së dytë — dhe është më e vështirë t'i përdorim sesa komandat native. Për të barazuar të drejtat e tyre, duhet të zgjidhen problemet e listuara.
Shells të funksioneve PowerShell
Me anë të shells të funksioneve PowerShell ne mund të shtojmë autocompletion të komandave dhe të eliminojmë nevojën për prefiksat wsl, duke e përkthyer rrugët e Windows në rrugë WSL. Kërkesat kryesore për shells janë:
- Për çdo komandë Linux duhet të ketë një shell funksioni me të njëjtin emër.
- Shell duhet të njohë rrugët e Windows të dërguara si argumente dhe t'i konvertojë ato në rrugë WSL.
- Shell duhet të thërrasë
wslme komandën përkatëse Linux për çdo hyrje të tubit dhe të kalojë çdo argument komandë të lëshuar në funksion.
Duke qenë se ky model mund të aplikohet për çdo komandë, ne mund të abstrahojmë përkufizimin e këtyre shells dhe t'i gjenerojmë dinamikisht nga një listë komandash për 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 ' ')
}
}
"@
} Lista $command përcakton komandat për import. Pastaj ne gjenerojmë dinamikisht një mbështjellës funksioni për secilën prej tyre, duke përdorur komandën Invoke-Expression (më parë duke hequr çdo alias që do të shkaktonte konflikte me funksionin).
Funksioni shqyrton argumentet e linjës së komandës, përcakton rrugët Windows me komandë Split-Path dhe Test-Path, dhe më pas i konverton këto rrugë në rrugë WSL. Ne ekzekutojmë rrugët përmes një funksioni ndihmës Format-WslArgument, që do ta përcaktojmë më vonë. Ajo shënon karakteret speciale, si hapësirat dhe kavillet, të cilat përndryshe do të interpretoheshin gabimisht.
Më në fund, ne kalojmë wsl të dhënat e pipeline dhe çdo argument linje komandë.
Me mbështjellës të tillë, mund të thërrisni komandat tuaja të preferuara Linux në një mënyrë më natyrale, pa shtuar prefix wsl dhe pa u shqetësuar për mënyrën se si kthehen rrugët:
man bashless -i $profile.CurrentUserAllHostsls -Al C:Windows | lessgrep -Ein error *.logtail -f *.log
Këtu është një set bazik komandesh, por mund të krijoni një shell për çdo komandë Linux, duke e shtuar atë vetëm në listë. Nëse e shtoni këtë kod në PowerShell, këto komanda do t'ju jenë të dala në çdo seancë PowerShell, ashtu si komandat natyrore!
Parametrat e paracaktuar
Në Linux është zakon të përcaktohen aliatët dhe/apo variablat e mjedisit në profile (login profile), duke caktuar parametrat e paracaktuar për komandat që përdoren shpesh (p.sh., alias ls=ls -AFh ose export LESS=-i). Një nga disavantazhet e përdorjes së proxy në një shell jo-interaktive wsl.exe — është se profilet nuk ngarkohen, prandaj këto parametra nuk janë të disponueshëm si parazgjedhje (p.sh. ls në WSL dhe wsl ls do të sillen ndryshe me aliasin e përcaktuar më sipër).
PowerShell ofron , një mekanizëm standard për përcaktimin e parametrave të parazgjedhur, por vetëm për cmdlet dhe funksione të avancuara. Natyrisht, mund të bëjmë funksione të avancuara nga shell-et tona, por kjo sjell komplikime të teperta (kështu, PowerShell përputhet me emrat e pjesshëm të parametrave (p.sh. -a përputhet me -ArgumentList), që do të bien në konflikt me komandat Linux që pranojnë emra të pjesshëm si argumento), dhe sintaksa për përcaktimin e vlerave të parazgjedhura nuk do të jetë më e përshtatshme (për përcaktimin e argumenteve të parazgjedhura kërkohet emri i parametrave në çelës, dhe jo vetëm emri i komandës).
Megjithatë, me një modifikim të vogël të shell-eve tona, ne mund të inkuadrojmë një model, të ngjashëm $PSDefaultParameterValues, dhe të përfshijmë parametrat e parazgjedhur për komandat Linux!
funksioni global:$_() {
…
`$defaultArgs = ((`$WslDefaultParameterValues.$_ -split ' '), "")[`$WslDefaultParameterValues.Disabled -eq `$true]
nëse (`$input.MoveNext()) {
`$input.Reset()
`$input | wsl.exe $_ `$defaultArgs (`$args -split ' ')
} tjetër {
wsl.exe $_ `$defaultArgs (`$args -split ' ')
}
} Duke kaluar $WslDefaultParameterValues në komandën e linjës, ne dërgojmë parametrat përmes wsl.exe. Më poshtë tregohet si të shtoni udhëzime në profilin PowerShell për të konfiguruar parametrat e parazgjedhur. Tani mundemi ta bëjmë këtë!
$WslDefaultParameterValues["grep"] = "-E"
$WslDefaultParameterValues["less"] = "-i"
$WslDefaultParameterValues["ls"] = "-AFh --group-directories-first" Për shkak se parametrat modelohen pas $PSDefaultParameterValues, mund të për një kohë të shkurtër, duke vendosur çelësin "Disabled" në vlerë $true. Një avantazh tjetër i një tabelë hash të veçantë është mundësia për të çaktivizuar $WslDefaultParameterValues veçmas nga $PSDefaultParameterValues.
Autopërfundimi i argumenteve
PowerShell lejon regjistrimin e përfunduesve të argumenteve me komandën Register-ArgumentCompleter. Në Bash ka mjete të fuqishme . WSL lejon mund të thërrasë bash nga PowerShell. Nëse mund të regjistrojmë përfunduesit e argumenteve për funksionet tona të PowerShell dhe të thërrasim bash për të krijuar përfundime, do të kemi përfundimin e plotë të argumenteve me të njëjtin saktësi si në vetë 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]
}
}Kodi është pak i ngjeshur pa kuptimin e disa funksioneve të brendshme të bash, por në tërësi ne bëjmë këtë:
- Regjistrojmë përfunduesin e argumenteve për të gjitha mbështjellësit tanë të funksioneve, duke kaluar një listë
$commandsnë parametrin-CommandNamepërRegister-ArgumentCompleter. - Ne përputhim çdo komandë me funksionin e mbështjellësit që përdor bash për përfundim (për të përcaktuar specifikimet e përfundimit në bash përdoret
$F, shkurtim ngacomplete -F). - Transformojmë argumentet e PowerShell
$wordToComplete,$commandAstdhe$cursorPositionnë formatin e pritur nga funksionet e përfundimit të bash në përputhje me specifikimet e bash. - Krijojmë komandën që do të kalojmë në
wsl.exe, e cila siguron konfigurimin e duhur të mjedisit, thërret funksionin përkatës të përfundimit dhe shfaq rezultatet me ndarjen në rreshta. - Pastaj thërrasim
wslme komandën, ndajmë daljen me ndarës të rreshtave dhe gjenerojmë për secilënRezultatet e Shkurtimit, duke i renditur ato dhe duke e ngushtuar karakteret, si hapësirat dhe kllapat, të cilat në rastin tjetër do të interpretoheshin gabimisht.
Në fund, shell-at tanë të komandës Linux do të përdorin të njëjtën autoshkurtim si në bash! Për shembull:
ssh -c -J -m -O -o -Q -w -b
Çdo autoshkurtim ofron vlera specifike për argumentin e mëparshëm, duke lexuar të dhënat e konfigurimit, siç janë hostet e njohur, nga WSL!
<TAB> do të kalojë ciklikisht përmes parametrave. <Ctrl + пробел> do të tregojë të gjitha opsionet në dispozicion.
Për më tepër, pasi tani kemi autoshkurtim bash, mund të autoshkurtosh rrugët Linux direkt në PowerShell!
less /etc/ls /usr/share/vim ~/ .bash
Në rastet kur autoshkurtimi bash nuk jep asnjë rezultat, PowerShell kthehet në sistemin e paracaktuar me rrugët Windows. Kështu, në praktikë mund të përdorësh të dyja rrugët sipas dëshirës.
Përfundimi
Me ndihmën e PowerShell dhe WSL, ne mund të integrojmë komandat Linux në Windows si aplikacione native. Nuk ka nevojë të kërkoni për ndërtimet Win32 ose mjete Linux, ose për të ndërprerë punën duke kaluar në shell-in Linux. Thjesht , konfiguroni dhe ! Plotësimi i pasur automatik për opsionet e komandave dhe rrugët e skedave Linux dhe Windows — kjo është një funksionalitet që sot nuk ekziston as në komandat native të Windows.
Kodi burimor i plotë, i përshkruar më sipër, si dhe rekomandime të mëtejshme për ta përfshirë atë në procesin e punës janë në dispozicion .
Cilat komanda Linux i konsideroni më të dobishme? Çfarë gjërash të tjera të zakonshme u mungojnë kur punoni në Windows? Shkruani në komentet më poshtë ose !
Burimi: habr.com
