PowerShell жана WSL аркылуу Linux буйруктарын Windows менен интеграциялоо

Windows иштеп чыгуучуларынын типтүү суроосу: “Эмне үчүн дагы деле жок <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?. Бул күчтүү серпүү болобу less же тааныш куралдар grep же sed, Windows иштеп чыгуучулары күнүмдүк иштеринде бул буйруктарга оңой жетүүнү каалашат.

Linux үчүн Windows субсистемасы (WSL) бул жагынан алга карай зор кадам жасады. Ал Windows'тан Linux буйруктарын прокси аркылуу чакырууга мүмкүндүк берет wsl.exe (Мис, wsl ls). Бул олуттуу жакшыртуу болсо да, бул параметр бир катар кемчиликтери бар.

  • Бардык жерде кошумча wsl тажатма жана табигый эмес.
  • Аргументтердеги Windows жолдору дайыма иштебейт, анткени арткы сызыктар каталогду бөлүүчү эмес, качуу символдору катары чечмеленет.
  • Аргументтердеги Windows жолдору WSLдеги тиешелүү орнотуу чекитине которулбайт.
  • Демейки жөндөөлөр лакап аттары жана чөйрө өзгөрмөлөрү бар WSL профилдеринде сакталбайт.
  • Linux жолун аяктоо колдоого алынбайт.
  • Буйрукту аяктоо колдоого алынбайт.
  • Аргументти аяктоо колдоого алынбайт.

Натыйжада, Linux буйруктары Windows астында экинчи класстын жарандары катары каралат жана жергиликтүү буйруктарга караганда колдонуу кыйыныраак. Алардын укуктарын теңдештирүү үчүн саналып өткөн көйгөйлөрдү чечүү зарыл.

PowerShell функциясынын орогучтары

PowerShell функционалдуу орогучтары менен биз буйруктун аяктоосун кошуп, префикстерге болгон муктаждыкты жок кыла алабыз wsl, Windows жолдорун WSL жолдоруна которуу. Капкаларга негизги талаптар:

  • Ар бир Linux буйругу үчүн бир эле ат менен бир функция орогуч болушу керек.
  • Кабк аргумент катары өткөн Windows жолдорун таанып, аларды WSL жолдоруна айландырышы керек.
  • Кап чалыш керек wsl каалаган түтүк киргизүүгө тиешелүү Linux буйругу менен жана функцияга өткөн каалаган команда сабынын аргументтерин өткөрүү.

Бул үлгү каалаган буйрукка колдонулушу мүмкүн болгондуктан, биз бул орогучтардын аныктамасын абстракциялап, аларды импорттоо үчүн буйруктардын тизмесинен динамикалык түрдө түзө алабыз.

# 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 ' ')
    }
}
"@
}

тизме $command импорттоо буйруктарын аныктайт. Андан кийин биз динамикалык түрдө буйрукту колдонуп, алардын ар бири үчүн функция орогучту түзөбүз Invoke-Expression (биринчи функцияга карама-каршы келе турган лакап аттарды алып салуу менен).

Функция буйрук сабынын аргументтерин кайталайт, буйруктарды колдонуу менен Windows жолдорун аныктайт Split-Path и Test-Pathанан бул жолдорду WSL жолдоруна айлантат. Биз жолдорду жардамчы функция аркылуу иштетебиз Format-WslArgument, аны биз кийинчерээк аныктайбыз. Болбосо туура эмес чечмеленүүчү боштуктар жана кашаалар сыяктуу өзгөчө белгилерден качат.

Акыры, биз жеткиребиз wsl түтүк киргизүү жана ар кандай буйрук сабынын аргументтери.

Бул орогучтар менен сиз сүйүктүү Linux буйруктарына префикс кошпостон дагы табигый жол менен чалсаңыз болот wsl жана жолдор кантип айландырылат деп кабатырланбастан:

  • man bash
  • less -i $profile.CurrentUserAllHosts
  • ls -Al C:Windows | less
  • grep -Ein error *.log
  • tail -f *.log

Буйруктардын негизги топтому бул жерде көрсөтүлгөн, бирок сиз каалаган Linux буйругу үчүн жөн гана тизмеге кошуу менен кабык түзө аласыз. Эгер сиз бул кодду өзүңүзгө кошсоңуз Перейти PowerShell, бул буйруктар жергиликтүү буйруктар сыяктуу эле ар бир PowerShell сессиясында сизге жеткиликтүү болот!

Демейки орнотуулар

Linux'та кирүү профилдеринде лакап аттарды жана/же чөйрө өзгөрмөлөрүн аныктоо, көп колдонулган буйруктар үчүн демейки параметрлерди коюу (мисалы, alias ls=ls -AFh же export LESS=-i). Интерактивдүү эмес кабык аркылуу проксисингдин кемчиликтеринин бири wsl.exe - профилдер жүктөлбөгөндүктөн, бул опциялар демейки боюнча жеткиликтүү эмес (б.а. ls WSLде жана wsl ls жогоруда аныкталган лакап ат менен башкача мамиле кылат).

PowerShell камсыз кылат $PSDefaultParameterValues, демейки параметрлерди аныктоо үчүн стандарттуу механизм, бирок командлеттер жана өркүндөтүлгөн функциялар үчүн гана. Албетте, биз кабыктарыбыздан өркүндөтүлгөн функцияларды жасай алабыз, бирок бул керексиз татаалдыктарга алып келет (мисалы, PowerShell жарым-жартылай параметр аталыштарын корреляциялайт (мисалы, -a менен байланышат -ArgumentList), ал жарым-жартылай аттарды аргумент катары алган Linux буйруктары менен карама-каршы келет) жана демейки маанилерди аныктоо үчүн синтаксис эң ылайыктуу болбойт (демейки аргументтер ачкычтагы параметрдин атын талап кылат, команданын аты эле эмес) .

Бирок, снаряддарыбызды бир аз өзгөртүү менен биз окшош моделди ишке ашыра алабыз $PSDefaultParameterValues, жана 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 ' ')
    }
}

жиберүү $WslDefaultParameterValues буйрук сабына, биз аркылуу параметрлерди жөнөтөбүз wsl.exe. Төмөндө демейки жөндөөлөрдү конфигурациялоо үчүн PowerShell профилиңизге нускамаларды кантип кошуу керектиги көрсөтүлгөн. Эми биз муну кыла алабыз!

$WslDefaultParameterValues["grep"] = "-E"
$WslDefaultParameterValues["less"] = "-i"
$WslDefaultParameterValues["ls"] = "-AFh --group-directories-first"

Параметрлер моделден кийин $PSDefaultParameterValues, Сенин колуңдан келет аларды өчүрүү оңой ачкычты орнотуу менен убактылуу "Disabled" мааниге $true. Өзүнчө хэш таблицанын кошумча пайдасы - бул өчүрүү $WslDefaultParameterValues тартып өзүнчө $PSDefaultParameterValues.

Аргумент бүтүрүү

PowerShell сизге буйрукту колдонуп аргумент трейлерлерин каттоого мүмкүндүк берет Register-ArgumentCompleter. Баш күчтүү программалоочу автоматтык аяктоо куралдары. WSL сизге PowerShellден bash чалуу мүмкүнчүлүгүн берет. Эгерде биз PowerShell функциясынын орогучтары үчүн аргументтердин аяктоолорун каттай алсак жана аяктоолорду жаратуу үчүн bash чакыра алсак, анда биз 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]
    }
}

Код bashдын кээ бир ички функцияларын түшүнбөстөн бир аз тыгыз, бирок, негизинен, биз муну жасайбыз:

  • Тизмени өткөрүү менен биздин бардык функциялар үчүн аргумент толуктоочуну каттоо $commands параметрде -CommandName үчүн Register-ArgumentCompleter.
  • Биз ар бир буйрукту bash автоматтык түрдө толтуруу үчүн колдонгон кабык функциясына түшүрөбүз (автоматтык бүтүрүү спецификацияларын аныктоо үчүн, bash колдонот $F, үчүн аббревиатура complete -F <FUNCTION>).
  • PowerShell аргументтерин конвертациялоо $wordToComplete, $commandAst и $cursorPosition спецификацияларга ылайык bash'тин автотолтуруучу функциялары күткөн форматка программалоочу автоматтык аяктоо баш.
  • Биз өткөрүп берүү үчүн буйрук сабын түзөбүз wsl.exe, бул айлана-чөйрөнүн туура орнотулушун камсыздайт, тиешелүү авто-толуктоо функциясын чакырат жана натыйжаларды сап-сапка чыгарат.
  • Анан чалабыз wsl буйрук сабы менен биз чыгарууну сап бөлгүчтөр менен бөлүп, ар бири үчүн жаратабыз CompletionResults, аларды сорттоо жана боштуктар жана кашаалар сыяктуу башка учурларда туура эмес чечмеленүүчү белгилерден качуу.

Натыйжада, биздин Linux командалык кабыктары bash сыяктуу автотолтурууну колдонот! Мисалы:

  • ssh -c <TAB> -J <TAB> -m <TAB> -O <TAB> -o <TAB> -Q <TAB> -w <TAB> -b <TAB>

Ар бир автотолуктоо WSLден белгилүү хосттор сыяктуу конфигурация маалыматтарын окуп, мурунку аргументке тиешелүү маанилерди берет!

<TAB> параметрлер боюнча айланышат. <Ctrl + пробел> бардык мүмкүн болгон параметрлерди көрсөтөт.

Мындан тышкары, бизде bash автотолтуруусу болгондуктан, Linux жолдорун түздөн-түз PowerShellде автотолтурсаңыз болот!

  • less /etc/<TAB>
  • ls /usr/share/<TAB>
  • vim ~/.bash<TAB>

Bash автотолтуруусу эч кандай натыйжа бербеген учурларда, PowerShell системанын демейки Windows жолдоруна кайтып келет. Ошентип, иш жүзүндө, сиз бир эле учурда өз каалоосу боюнча эки жолду колдоно аласыз.

жыйынтыктоо

PowerShell жана WSLди колдонуу менен биз Linux буйруктарын Windows'ка жергиликтүү тиркемелер катары бириктире алабыз. Win32 түзүмдөрүн же Linux утилиталарын издөөнүн же Linux кабыгына өтүү менен иш процессиңизди үзүүнүн кереги жок. Жөн гана WSL орнотуу, конфигурациялоо PowerShell профили и импорттогуңуз келген буйруктарды тизмектеңиз! Linux жана Windows командалык параметрлери жана файл жолдору үчүн бай автотолуктоо бүгүнкү күндө жергиликтүү Windows буйруктарында да жок функция.

Жогоруда сүрөттөлгөн толук баштапкы код, ошондой эле аны иш процессиңизге киргизүү боюнча кошумча көрсөтмөлөр бар бул жерде.

Кайсы Linux буйруктарын эң пайдалуу деп эсептейсиз? Windowsта иштөөдө дагы кандай жалпы нерселер жетишпейт? Комментарийге жазыңыз же GitHub боюнча!

Source: www.habr.com

Комментарий кошуу