PowerShell va WSL yordamida Linux buyruqlarini Windowsga integratsiya qilish

Windows dasturchilarining odatiy savoli: “Nega hali ham yo'q <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?. Bu kuchli surish bo'ladimi less yoki tanish vositalar grep yoki sed, Windows ishlab chiquvchilari kundalik ishlarida ushbu buyruqlarga oson kirishni xohlashadi.

Linux uchun Windows quyi tizimi (WSL) bu borada katta qadam tashladi. Bu sizga Windows-dan Linux buyruqlarini proksi-server orqali chaqirish imkonini beradi wsl.exe (masalan, wsl ls). Bu sezilarli yaxshilanish bo'lsa-da, bu variant bir qator kamchiliklarga ega.

  • Hamma joyda qo'shilish wsl zerikarli va g'ayritabiiy.
  • Argumentlardagi Windows yo'llari har doim ham ishlamaydi, chunki teskari qiyshiq chiziqlar katalog ajratgichlari emas, balki qochish belgilari sifatida talqin etiladi.
  • Argumentlardagi Windows yo'llari WSL da mos keladigan o'rnatish nuqtasiga tarjima qilinmaydi.
  • Taxalluslar va muhit oʻzgaruvchilari boʻlgan WSL profillarida standart sozlamalarga rioya etilmaydi.
  • Linux yoʻlini yakunlash qoʻllab-quvvatlanmaydi.
  • Buyruqni bajarish qo'llab-quvvatlanmaydi.
  • Argumentni yakunlash qo‘llab-quvvatlanmaydi.

Natijada, Linux buyruqlari Windows ostida ikkinchi darajali fuqarolar kabi ko'rib chiqiladi va ulardan foydalanish mahalliy buyruqlarga qaraganda ancha qiyin. Ularning huquqlarini tenglashtirish uchun yuqorida sanab o'tilgan muammolarni hal qilish kerak.

PowerShell funktsiyasini o'rash

PowerShell funktsiyasini o'rash vositalari bilan biz buyruqlarni bajarishni qo'shishimiz va prefikslarga bo'lgan ehtiyojni bartaraf etishimiz mumkin wsl, Windows yo'llarini WSL yo'llariga tarjima qilish. Chig'anoqlarga qo'yiladigan asosiy talablar:

  • Har bir Linux buyrug'i uchun bir xil nomga ega bo'lgan bitta funktsiya o'rami bo'lishi kerak.
  • Qobiq argument sifatida uzatilgan Windows yo'llarini tanib olishi va ularni WSL yo'llariga aylantirishi kerak.
  • Qobiq qo'ng'iroq qilishi kerak wsl tegishli Linux buyrug'i bilan har qanday quvur liniyasi kiritish va funktsiyaga o'tkazilgan har qanday buyruq qatori argumentlarini uzatish.

Ushbu naqsh har qanday buyruqqa qo'llanilishi mumkinligi sababli, biz ushbu o'ramlarning ta'rifini mavhumlashimiz va ularni import qilish uchun buyruqlar ro'yxatidan dinamik ravishda yaratishimiz mumkin.

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

ro'yxat $command import buyruqlarini belgilaydi. Keyin buyruq yordamida dinamik ravishda ularning har biri uchun funktsiya o'ramini hosil qilamiz Invoke-Expression (avval funktsiyaga zid keladigan taxalluslarni olib tashlash orqali).

Funktsiya buyruq qatori argumentlari bo'yicha takrorlanadi, buyruqlar yordamida Windows yo'llarini aniqlaydi Split-Path и Test-Pathva keyin bu yo'llarni WSL yo'llariga aylantiradi. Yo'llarni yordamchi funksiya orqali boshqaramiz Format-WslArgument, biz buni keyinroq aniqlaymiz. U bo'shliqlar va qavslar kabi maxsus belgilardan qochadi, aks holda noto'g'ri talqin qilinadi.

Nihoyat, etkazamiz wsl quvur liniyasi kiritish va har qanday buyruq qatori argumentlari.

Ushbu o'ramlar yordamida siz o'zingizning sevimli Linux buyruqlaringizni prefiks qo'shmasdan tabiiyroq tarzda chaqirishingiz mumkin wsl va yo'llar qanday o'zgartirilishi haqida tashvishlanmasdan:

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

Bu erda asosiy buyruqlar to'plami ko'rsatilgan, ammo siz uni ro'yxatga qo'shish orqali istalgan Linux buyrug'i uchun qobiq yaratishingiz mumkin. Agar siz ushbu kodni o'zingizga qo'shsangiz profil PowerShell, bu buyruqlar mahalliy buyruqlar kabi har bir PowerShell sessiyasida siz uchun mavjud bo'ladi!

Standart sozlamalar

Linux-da login profillarida taxalluslar va/yoki muhit o'zgaruvchilarini aniqlash, tez-tez ishlatiladigan buyruqlar uchun standart parametrlarni o'rnatish odatiy holdir (masalan, alias ls=ls -AFh yoki export LESS=-i). Interaktiv bo'lmagan qobiq orqali proksi-serverning kamchiliklaridan biri wsl.exe - profillar yuklanmaganligi, shuning uchun bu parametrlar sukut bo'yicha mavjud emas (ya'ni. ls WSLda va wsl ls yuqorida aniqlangan taxallus bilan boshqacha harakat qiladi).

PowerShell taqdim etadi $PSDefaultParameterValues, standart parametrlarni aniqlash uchun standart mexanizm, lekin faqat cmdletlar va rivojlangan funktsiyalar uchun. Albatta, biz qobiqlarimizdan ilg'or funktsiyalarni yaratishimiz mumkin, ammo bu keraksiz asoratlarni keltirib chiqaradi (masalan, PowerShell qisman parametr nomlarini korrelyatsiya qiladi (masalan, -a bilan korrelyatsiya qiladi -ArgumentList), bu qisman nomlarni argument sifatida qabul qiladigan Linux buyruqlariga zid keladi) va standart qiymatlarni aniqlash sintaksisi eng mos bo'lmaydi (standart argumentlar faqat buyruq nomini emas, balki kalitdagi parametr nomini talab qiladi) .

Biroq, qobiqlarimizni biroz o'zgartirish bilan biz shunga o'xshash modelni amalga oshirishimiz mumkin $PSDefaultParameterValues, va Linux buyruqlari uchun standart variantlarni yoqing!

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

O'tmoqda $WslDefaultParameterValues buyruq satriga parametrlarni orqali yuboramiz wsl.exe. Quyida standart sozlamalarni sozlash uchun PowerShell profilingizga ko'rsatmalar qo'shish ko'rsatilgan. Endi biz qila olamiz!

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

Parametrlar modellashtirilganligi sababli $PSDefaultParameterValues, Siz .. qila olasiz; siz ... mumkin ularni o'chirish oson kalitni o'rnatish orqali vaqtincha "Disabled" ma'noga kiradi $true. Alohida hash-jadvalning qo'shimcha afzalligi - o'chirish qobiliyati $WslDefaultParameterValues dan alohida $PSDefaultParameterValues.

Argumentni yakunlash

PowerShell buyrug'i yordamida argument treylerini ro'yxatdan o'tkazish imkonini beradi Register-ArgumentCompleter. Bash kuchli dasturlashtiriladigan avtomatik yakunlash vositalari. WSL sizga PowerShell-dan bash-ga qo'ng'iroq qilish imkonini beradi. Agar biz PowerShell funksiyasi oʻramlari uchun argumentlar toʻldirilishini roʻyxatdan oʻtkaza olsak va toʻldirishlarni yaratish uchun bash ga qoʻngʻiroq qilsak, bashning oʻzi kabi aniqlik bilan toʻliq argument tugallanishiga erishamiz!

# 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 bashning ba'zi ichki funktsiyalarini tushunmasdan biroz zich, lekin biz asosan shunday qilamiz:

  • Roʻyxatni oʻtkazish orqali barcha funktsiya oʻramlarimiz uchun argument toʻldiruvchini roʻyxatdan oʻtkazish $commands parametrda -CommandName uchun Register-ArgumentCompleter.
  • Biz har bir buyruqni bash avtomatik to‘ldirish uchun ishlatadigan qobiq funksiyasiga moslashtiramiz (avtomatik to‘ldirish spetsifikatsiyalarini aniqlash uchun, bash foydalanadi). $F, qisqartmasi complete -F <FUNCTION>).
  • PowerShell argumentlarini konvertatsiya qilish $wordToComplete, $commandAst и $cursorPosition spetsifikatsiyalarga muvofiq bashning avtoto'ldirish funksiyalari kutgan formatga dasturlashtiriladigan avtomatik yakunlash bash.
  • O'tkazish uchun buyruq qatorini tuzamiz wsl.exe, bu atrof-muhitning to'g'ri o'rnatilishini ta'minlaydi, tegishli avtomatik yakunlash funktsiyasini chaqiradi va natijalarni satr bo'yicha chiqaradi.
  • Keyin qo'ng'iroq qilamiz wsl buyruq qatori bilan biz chiqishni qator ajratgichlar bilan ajratamiz va har biri uchun hosil qilamiz CompletionResults, ularni tartiblash va aks holda noto'g'ri talqin qilinishi mumkin bo'lgan bo'shliqlar va qavslar kabi belgilardan qochish.

Natijada, bizning Linux buyruq qobiqlarimiz bash bilan bir xil avtomatik to'ldirishdan foydalanadi! Masalan:

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

Har bir avtomatik to'ldirish WSL-dan ma'lum xostlar kabi konfiguratsiya ma'lumotlarini o'qib, oldingi argumentga xos qiymatlarni taqdim etadi!

<TAB> parametrlar bo'ylab aylanadi. <Ctrl + пробел> barcha mavjud variantlarni ko'rsatadi.

Bundan tashqari, bizda bash avtoto‘ldirish funksiyasi mavjud bo‘lgani uchun siz Linux yo‘llarini to‘g‘ridan-to‘g‘ri PowerShell’da avtomatik to‘ldirishingiz mumkin!

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

Bash avtoto'ldirish hech qanday natija bermasa, PowerShell tizimning standart Windows yo'llariga qaytadi. Shunday qilib, amalda siz o'zingizning xohishingiz bilan bir vaqtning o'zida ikkala yo'ldan ham foydalanishingiz mumkin.

xulosa

PowerShell va WSL-dan foydalanib, biz Linux buyruqlarini Windows-ga mahalliy ilovalar sifatida birlashtira olamiz. Win32 tuzilmalari yoki Linux yordam dasturlarini qidirish yoki Linux qobig'iga o'tish orqali ish jarayonini to'xtatishning hojati yo'q. Shunchaki WSL-ni o'rnating, sozlang PowerShell profili и import qilmoqchi bo'lgan buyruqlarni ro'yxatlang! Linux va Windows buyruq parametrlari va fayl yo'llari uchun boy avtomatik to'ldirish bugungi kunda Windowsning mahalliy buyruqlarida ham mavjud bo'lmagan funksionallikdir.

Yuqorida tavsiflangan to'liq manba kodi, shuningdek uni ish oqimingizga kiritish bo'yicha qo'shimcha ko'rsatmalar mavjud shu yerda.

Qaysi Linux buyruqlari sizga eng foydali deb hisoblaysiz? Windowsda ishlashda yana qanday umumiy narsalar etishmayapti? Izohlarda yozing yoki GitHub-da!

Manba: www.habr.com

a Izoh qo'shish