PowerShell və WSL istifadə edərək Linux əmrlərinin Windows-a inteqrasiyası

Windows tərtibatçılarının tipik sualı: “Niyə hələ də yoxdur <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?. İstər güclü sürüşdürmə olsun less və ya tanış alətlər grep və ya sed, Windows tərtibatçıları gündəlik işlərində bu əmrlərə asanlıqla daxil olmaq istəyirlər.

Linux üçün Windows Alt Sistemi (WSL) bu istiqamətdə böyük addım atmışdır. Bu, Windows-dan Linux əmrlərinə onları proksiləşdirərək zəng etməyə imkan verir wsl.exe (Məsələn, wsl ls). Bu əhəmiyyətli bir irəliləyiş olsa da, bu seçim bir sıra mənfi cəhətlərdən əziyyət çəkir.

  • Hər yerdə əlavə wsl yorucu və qeyri-təbii.
  • Arqumentlərdəki Windows yolları həmişə işləmir, çünki əks xətlər kataloq ayırıcıları deyil, qaçış simvolları kimi şərh olunur.
  • Arqumentlərdəki Windows yolları WSL-də müvafiq quraşdırma nöqtəsinə tərcümə edilmir.
  • Ləqəblər və mühit dəyişənləri olan WSL profillərində defolt parametrlərə riayət edilmir.
  • Linux yolunun tamamlanması dəstəklənmir.
  • Komandanın tamamlanması dəstəklənmir.
  • Arqument tamamlama dəstəklənmir.

Nəticədə, Linux əmrləri Windows altında ikinci dərəcəli vətəndaşlar kimi qəbul edilir və yerli əmrlərdən istifadə etmək daha çətindir. Onların hüquqlarını bərabərləşdirmək üçün sadalanan problemləri həll etmək lazımdır.

PowerShell funksiyası sarğıları

PowerShell funksiyası sarğıları ilə biz əmr tamamlama əlavə edə və prefikslərə ehtiyacı aradan qaldıra bilərik wsl, Windows yollarını WSL yollarına çevirmək. Qabıqlar üçün əsas tələblər:

  • Hər bir Linux əmri üçün eyni adlı bir funksiya sarğısı olmalıdır.
  • Qabıq arqumentlər kimi ötürülən Windows yollarını tanımalı və onları WSL yollarına çevirməlidir.
  • Qabıq zəng etməlidir wsl müvafiq Linux əmri ilə istənilən boru xətti girişinə və funksiyaya ötürülən hər hansı əmr xətti arqumentlərini ötürə bilərsiniz.

Bu nümunə istənilən əmrə tətbiq oluna bildiyi üçün biz bu sarğıların tərifini mücərrədləşdirə və onları idxal ediləcək əmrlər siyahısından dinamik şəkildə yarada bilərik.

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

Siyahı $command idxal əmrlərini müəyyən edir. Daha sonra komandadan istifadə edərək dinamik olaraq onların hər biri üçün funksiya sarğısını yaradırıq Invoke-Expression (əvvəlcə funksiya ilə ziddiyyət təşkil edəcək hər hansı ləqəbləri silməklə).

Funksiya komanda xətti arqumentləri üzərində təkrarlanır, əmrlərdən istifadə edərək Windows yollarını müəyyən edir Split-Path и Test-Pathvə sonra bu yolları WSL yollarına çevirir. Biz yolları köməkçi funksiyadan keçirik Format-WslArgumentsonra müəyyən edəcəyik. O, boşluqlar və mötərizələr kimi xüsusi simvollardan qaçır və əks halda yanlış təfsir edilir.

Nəhayət, çatdırırıq wsl boru kəməri girişi və hər hansı komanda xətti arqumentləri.

Bu sarğılarla siz sevimli Linux əmrlərinə prefiks əlavə etmədən daha təbii şəkildə zəng edə bilərsiniz wsl və yolların necə çevrildiyindən narahat olmadan:

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

Əsas əmrlər dəsti burada göstərilir, lakin siz sadəcə siyahıya əlavə etməklə istənilən Linux əmri üçün qabıq yarada bilərsiniz. Bu kodu özünüzə əlavə etsəniz profil PowerShell, bu əmrlər yerli əmrlər kimi hər PowerShell sessiyasında sizin üçün əlçatan olacaq!

Defolt Parametrlər

Linux-da giriş profillərində ləqəblərin və/yaxud mühit dəyişənlərinin müəyyən edilməsi, tez-tez istifadə olunan əmrlər üçün standart parametrlərin təyin edilməsi adi haldır (məsələn, alias ls=ls -AFh və ya export LESS=-i). Qeyri-interaktiv qabıq vasitəsilə proxyin çatışmazlıqlarından biri wsl.exe - profillərin yüklənməməsi, buna görə də bu seçimlər standart olaraq mövcud deyil (yəni. ls WSL və wsl ls yuxarıda müəyyən edilmiş ləqəblə fərqli davranacaq).

PowerShell təmin edir $PSDefaultParameterValues, standart parametrləri təyin etmək üçün standart mexanizm, lakin yalnız cmdletlər və qabaqcıl funksiyalar üçün. Əlbəttə ki, qabıqlarımızdan qabaqcıl funksiyalar yarada bilərik, lakin bu, lazımsız fəsadlar yaradır (məsələn, PowerShell qismən parametr adlarını əlaqələndirir (məsələn, -a ilə əlaqələndirilir -ArgumentList), qismən adları arqument kimi qəbul edən Linux əmrləri ilə ziddiyyət təşkil edəcək) və standart dəyərləri təyin etmək üçün sintaksis ən uyğun olmayacaq (defolt arqumentlər yalnız əmr adını deyil, açarda parametrin adını tələb edir) .

Bununla belə, qabıqlarımıza bir az dəyişiklik etməklə, buna bənzər bir modeli həyata keçirə bilərik $PSDefaultParameterValues, və Linux əmrləri üçün standart seçimləri aktivləşdirin!

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 ' ')
    }
}

Keçir $WslDefaultParameterValues komanda xəttinə parametrləri vasitəsilə göndəririk wsl.exe. Aşağıda standart parametrləri konfiqurasiya etmək üçün PowerShell profilinizə təlimatların necə əlavə olunacağı göstərilir. İndi biz bunu edə bilərik!

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

Parametrlər modelləşdirildiyi üçün $PSDefaultParameterValuesedə bilərsiniz onları söndürmək asandır açarı quraşdıraraq müvəqqəti olaraq "Disabled" mənaya keçir $true. Ayrı bir hash cədvəlinin əlavə faydası söndürmək qabiliyyətidir $WslDefaultParameterValues -dən ayrı $PSDefaultParameterValues.

Arqumentin tamamlanması

PowerShell, əmrdən istifadə edərək arqument treylerini qeydiyyatdan keçirməyə imkan verir Register-ArgumentCompleter. Bash güclüdür proqramlaşdırıla bilən avtomatik tamamlama vasitələri. WSL sizə PowerShell-dən bash çağırmağa imkan verir. PowerShell funksiyası sarğılarımız üçün arqument tamamlamalarını qeyd edə bilsək və tamamlamaları yaratmaq üçün bash çağıra bilsək, bash ilə eyni dəqiqliklə tam arqument tamamlanmasını əldə edirik!

# 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]
    }
}

Kod bash-ın bəzi daxili funksiyalarını başa düşmədən bir qədər sıxdır, lakin əsas olaraq etdiyimiz budur:

  • Siyahıdan keçməklə bütün funksiya sarğılarımız üçün arqument tamamlayıcısının qeydiyyatı $commands parametrə daxil olur -CommandName uğrunda Register-ArgumentCompleter.
  • Biz hər əmri bash-ın avtomatik tamamlama üçün istifadə etdiyi qabıq funksiyasına uyğunlaşdırırıq (avtomatik tamamlama spesifikasiyalarını müəyyən etmək üçün, bash istifadə edir. $F, qısaltması complete -F <FUNCTION>).
  • PowerShell arqumentlərinin çevrilməsi $wordToComplete, $commandAst и $cursorPosition spesifikasiyalara uyğun olaraq bash-ın avtomatik tamamlama funksiyaları tərəfindən gözlənilən formata proqramlaşdırıla bilən avtomatik tamamlama bash.
  • Köçürmək üçün bir komanda xətti tərtib edirik wsl.exe, mühitin düzgün qurulmasını təmin edir, müvafiq avtomatik tamamlama funksiyasını çağırır və nəticələri sətir-sətir şəklində verir.
  • Sonra zəng edirik wsl komanda xətti ilə çıxışı sətir ayırıcıları ilə ayırırıq və hər biri üçün yaradırıq CompletionResults, onları çeşidləmək və boşluqlar və mötərizələr kimi simvollardan qaçmaq, əks halda yanlış şərh ediləcək.

Nəticədə, Linux əmr qabıqlarımız bash ilə eyni avtomatik tamamlamadan istifadə edəcək! Misal üçün:

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

Hər bir avtomatik tamamlama WSL-dən məlum hostlar kimi konfiqurasiya məlumatlarını oxuyaraq əvvəlki arqumentə xas olan dəyərləri təmin edir!

<TAB> parametrlər arasında dövr edəcək. <Ctrl + пробел> bütün mövcud variantları göstərəcək.

Üstəlik, indi bash avtomatik tamamlamamız olduğundan, siz Linux yollarını birbaşa PowerShell-də avtomatik tamamlaya bilərsiniz!

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

Bash avtomatik tamamlama heç bir nəticə vermədiyi hallarda, PowerShell sistemin standart Windows yollarına qayıdır. Beləliklə, praktikada siz öz mülahizənizlə eyni vaxtda hər iki yoldan istifadə edə bilərsiniz.

Nəticə

PowerShell və WSL istifadə edərək, biz Linux əmrlərini Windows-a yerli proqramlar kimi inteqrasiya edə bilərik. Win32 konstruksiyalarını və ya Linux yardım proqramlarını axtarmağa və ya Linux qabığına keçməklə iş prosesinizi dayandırmağa ehtiyac yoxdur. Sadəcə WSL quraşdırın, konfiqurasiya edin PowerShell profili и idxal etmək istədiyiniz əmrləri sadalayın! Linux və Windows əmr parametrləri və fayl yolları üçün zəngin avtomatik tamamlama bu gün yerli Windows əmrlərində belə mövcud olmayan funksionallıqdır.

Yuxarıda təsvir edilən tam mənbə kodu, eləcə də onu iş prosesinizə daxil etmək üçün əlavə təlimatlar mövcuddur. burada.

Hansı Linux əmrlərini ən faydalı hesab edirsiniz? Windows-da işləyərkən başqa hansı ümumi şeylər çatışmır? Şərhlərdə yazın və ya GitHub-da!

Mənbə: www.habr.com

Добавить комментарий