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. Bash күшті бағдарламаланатын автоматты аяқтау құралдары. 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 автотолтыру функциялары күткен пішімге бағдарламаланатын автоматты аяқтау 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 арналған!

Ақпарат көзі: www.habr.com

пікір қалдыру