Typická otázka vývojárov na Windows: „Prečo tu stále nie je <VLOŽTE SEM SVOJ OBLÚBENÝ PRÍKAZ LINUX>?». Будь то мощное пролистывание menej alebo zvyčajné nástroje grep alebo sed, vývojári na Windows chcú mať jednoduchý prístup k týmto príkazom vo svojej každodennej práci.
urobila obrovský pokrok v tomto smere. Umožňuje spúšťať príkazy Linuxu z Windows tým, že ich proxuje cez wsl.exe (napríklad, wsl ls). Aj keď je to významné zlepšenie, tento spôsob trpí množstvom nedostatkov.
- Zavedenie
wslje únavné a neprirodzené. - Cesty Windows v argumentoch sa ne vždy spracovávajú správne, pretože spätné lomítka sú interpretované ako escape znaky, a nie ako oddeľovače adresárov.
- Cesty Windows v argumentoch sa neprekladajú na zodpovedajúci bod pripojenia v WSL.
- Predvolené parametre v profiloch WSL s aliasmi a premennými prostredia sa nezohľadňujú.
- Nepodporuje sa autokompletácia ciest Linux.
- Nepodporuje sa autokompletácia príkazov.
- Nepodporuje sa autokompletácia argumentov.
V dôsledku toho sa príkazy Linuxu považujú na Windows za druhotriedne — a ich používanie je zložitejšie ako u natívnych príkazov. Aby sme im poskytli rovnaké práva, musíme riešiť uvedené problémy.
Funkčné obaly PowerShell
Pomocou funkčných obalov PowerShell môžeme pridať autokompletáciu príkazov a odstrániť potrebu prefixov wsl, prevodom ciest Windows na cesty WSL. Hlavné požiadavky na obaly sú:
- Pre každý príkaz Linux by mala existovať jedna funkcia s rovnakým názvom.
- Funkcia by mala rozpoznať cesty Windows predané ako argumenty a previesť ich na cesty WSL.
- Funkcia by mala volať
wsls príslušným príkazom Linux na akýkoľvek vstup potrubia a posielať akékoľvek argumenty príkazového riadku dodané funkcii.
Pretože tento vzor môže byť použitý pre 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 určuje príkazy na import. Potom dynamicky generujeme obálku funkcie pre každý z nich pomocou príkazu Invoke-Expression (najprv odstránime akékoľvek aliasy, ktoré by mohli konfliktovať s funkciou).
Funkcia prechádza argumentmi príkazového riadku, rozpoznáva cesty Windows pomocou príkazov Split-Path a Test-Path, a potom tieto cesty prevedie na WSL cesty. Spúšťame cesty cez pomocnú funkciu Format-WslArgument, ktorú definujeme neskôr. Zabezpečuje únik špeciálnych znakov, ako sú medzery a zátvorky, ktoré by inak boli nesprávne interpretované.
Nakoniec odovzdávame wsl vstupy potrubia a akékoľvek argumenty príkazového riadku.
Pomocou takýchto obalov môžete volat svoje obľúbené príkazy Linuxu prirodzenejším spôsobom, bez pridávania prefixu wsl a bez obáv o to, ako sa cesty prevádzajú:
man bashless -i $profile.CurrentUserAllHostsls -Al C:Windows | lessgrep -Ein error *.logtail -f *.log
Tu je základný súbor príkazov, ale môžete vytvoriť obal pre akýkoľvek príkaz Linuxu jednoducho tým, že ho pridáte do zoznamu. Ak tento kód pridáte do svojho PowerShell, tieto príkazy budú pre vás dostupné v každom sedení PowerShellu, rovnako ako natívne príkazy!
Predvolené parametre
V Linuxe je zvykom definovať aliasy a/alebo premenné prostredia v profiloch (login profile), čím sa nastavujú predvolené parametre pre často používané príkazy (napríklad alias ls=ls -AFh alebo export LESS=-i). Jednou z nevýhod proxy cez neinteraktívnu shell je wsl.exe to, že profily sa nenačítavajú, takže tieto predvolené parametre nie sú k dispozícii (t. j. ls v WSL a wsl ls budú sa správať inak s aliasom definovaným vyššie).
PowerShell poskytuje , štandardný mechanizmus na definovanie predvolených parametrov, ale iba pre cmdlet a rozšírené funkcie. Samozrejme, môžeme z našich shellov urobiť rozšírené funkcie, ale to prináša zbytočné komplikácie (tak, PowerShell spája čiastočné názvy parametrov (napríklad -a oboresť -ArgumentList), ktoré budú kolidovať s príkazmi Linuxu, ktoré prijímajú čiastočné názvy ako argumenty), a syntax na definovanie predvolených hodnôt nebude najvhodnejšia (na definovanie argumentov s predvolenými hodnotami je potrebné meno parametra v kľúči, nie len meno príkazu).
Avšak s malou zmenou našich shellov môžeme implementovať model podobný $PSDefaultParameterValues, a zahrnúť predvolené parametre pre príkazy Linuxu!
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 ' ')
}
} Odovzdávajúc $WslDefaultParameterValues do príkazového riadku posielame parametre cez wsl.exe. Nižšie je znázornené, ako pridať inštrukcie do profilu PowerShell na nastavenie predvolených parametrov. Teraz to môžeme urobiť!
$WslDefaultParameterValues["grep"] = "-E"
$WslDefaultParameterValues["less"] = "-i"
$WslDefaultParameterValues["ls"] = "-AFh --group-directories-first" Keďže sa parametre modelujú podľa $PSDefaultParameterValues, môžete dočasne nastavením kľúča "Disabled" na hodnotu $true. Ďalšou výhodou samostatnej hash tabuľky je schopnosť vypnúť $WslDefaultParameterValues samostatne od $PSDefaultParameterValues.
Doplnenia argumentov
PowerShell umožňuje registrovať dokončovače argumentov pomocou príkazu Register-ArgumentCompleter. V Bash existujú silné . WSL umožňuje vyvolať bash z PowerShell. Ak môžeme registrovať dokončovače argumentov pre naše shells funkcie PowerShell a zavolať bash na vytvorenie doplnení, dostaneme kompletné doplnenie argumentov s rovnakou presnosťou ako v samotnom 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í bash, ale vo funkcii robíme nasledovné:
- Registrujeme dokončovač argumentov pre všetky naše obálkové funkcie, pričom odovzdávame zoznam
$commandsdo parametra-CommandNamepreRegister-ArgumentCompleter. - Mapujeme každý príkaz na funkciu shellu, ktorú bash používa na doplnenie (na určenie špecifikácií doplnenia v bash sa používa
$F, skratka precomplete -F). - Transformujeme argumenty PowerShell
$wordToComplete,$commandAsta$cursorPositiondo formátu očakávaného funkciami doplnenia bash v súlade so špecifikáciami bash. - Skladáme príkazový riadok na odovzdanie do
wsl.exe, ktorý zabezpečuje správne nastavenie prostredia, vyvoláva príslušnú funkciu doplnenia a výstupy výsledkov s rozklady na riadky. - Potom voláme
wsls príkazovým riadkom, delíme výstup pomocou oddelovačov riadkov a generujeme pre každéCompletionResults, zoradením ich a escapovaním znakov, ako sú medzery a zátvorky, ktoré by inak boli nesprávne interpretované.
Na konci naše linuxové príkazové shells použijú presne to isté doplnenie, ako v bash! Napríklad:
ssh -c -J -m -O -o -Q -w -b
Každé automatické doplnenie poskytuje hodnoty špecifické pre predchádzajúci argument, čítaním konfiguračných údajov, ako sú známe hostiteľské mená, z WSL!
<TAB> bude cyklicky prechádzať parametre. <Ctrl + пробел> zobrazí všetky dostupné možnosti.
Okrem toho, keďže teraz máme funkčné doplnenie bash, môžete autocompletovať cesty Linuxu priamo v PowerShell!
less /etc/ls /usr/share/vim ~/ .bash
V prípadoch, keď doplnenie bash neposkytuje žiadne výsledky, PowerShell sa vracia k predvolenej systémovej ceste Windows. Takže môžete prakticky súčasne používať obidve cesty podľa vlastného uváženia.
Záver
S PowerShell a WSL môžeme integrovať príkazy Linuxu do Windows ako natívne aplikácie. Nie je potrebné hľadať Win32 zostavy alebo nástroje Linuxu alebo prerušovať pracovný tok prepínaním do linuxovej shell. , nastavte a ! Bohaté automatické doplnenie pre príkazové parametre a cesty k súborom Linux a Windows - je to funkčnosť, ktorú dnes nemajú dokonca ani natívne príkazy Windows.
Celý zdrojový kód, ktorý bol popísaný vyššie, ako aj dodatočné odporúčania na jeho zahrnutie do pracovného toku sú dostupné .
Ktoré príkazy Linuxu považujete za najužitočnejšie? Čo vám ešte chýba pri práci vo Windowse? Napíšte v komentároch alebo !
Zdroj: habr.com
