Linux komandoak Windows-en integratzea PowerShell eta WSL erabiliz

Windows garatzaileen ohiko galdera: "Zergatik ez dago oraindik ez <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>?. Irrista indartsua den ala ez less edo tresna ezagunak grep edo sed, Windows-eko garatzaileek komando hauetarako sarbide erraza nahi dute eguneroko lanean.

Linuxerako Windows azpisistema (WSL) ildo horretan aurrerapauso handia eman du. Windows-etik Linux-eko komandoak deitzeko aukera ematen du, proxy bidez wsl.exe (Adibidez, wsl ls). Hau hobekuntza nabarmena bada ere, aukera honek hainbat desabantaila ditu.

  • Nonahiko gain wsl neketsua eta ez-naturala.
  • Argumentuetan Windows-eko bideek ez dute beti funtzionatzen, atzera-barrak ihes-karaktere gisa interpretatzen direlako direktorio-bereizleak baino.
  • Argumentuetako Windows bideak ez dira WSL-n dagokion muntatze-puntura itzultzen.
  • Ezarpen lehenetsiak ez dira errespetatzen aliasak eta ingurune-aldagaiak dituzten WSL profiletan.
  • Linux-eko bidea osatzea ez da onartzen.
  • Komandoa osatzea ez da onartzen.
  • Ez da onartzen argudioak osatzea.

Ondorioz, Linux-eko komandoak bigarren mailako herritarrak bezala tratatzen dira Windows-en, eta erabiltzeko zailagoa da jatorrizko komandoak baino. Beren eskubideak berdintzeko, beharrezkoa da zerrendatutako arazoak konpontzea.

PowerShell funtzioen bilgarriak

PowerShell funtzioen bilgarriekin, komandoak osatzea gehi dezakegu eta aurrizkien beharra ezabatu wsl, Windows bideak WSL bideetara itzuliz. Maskorren oinarrizko baldintzak:

  • Linux komando bakoitzeko funtzio-bilgarri bat egon behar du izen bereko.
  • Shell-ak argumentu gisa igarotako Windows bideak ezagutu eta WSL bide bihurtu behar ditu.
  • Maskorrak deitu beharko luke wsl Linux komando egokiarekin edozein kanalizazio-sarrerari eta funtzioari emandako komando-lerroko argumentuak pasatuz.

Eredu hau edozein komandotan aplika daitekeenez, bilgarri hauen definizioa abstraitu eta inportatu beharreko komandoen zerrendatik dinamikoki sor ditzakegu.

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

Zerrenda $command inportazio komandoak definitzen ditu. Ondoren, dinamikoki funtzio-bilgarri bat sortzen dugu horietako bakoitzarentzat komandoa erabiliz Invoke-Expression (lehenik funtzioarekin gatazkan egongo diren aliasak kenduz).

Funtzioak komando-lerroko argumentuen gainean errepikatzen du, Windows bideak zehazten ditu komandoak erabiliz Split-Path и Test-Patheta gero bide hauek WSL bide bihurtzen ditu. Bideak laguntzaile funtzio baten bidez exekutatzen ditugu Format-WslArgument, geroago definituko duguna. Bestela gaizki interpretatuko liratekeen zuriuneak eta parentesiak bezalako karaktere berezietatik ihes egiten du.

Azkenik, transmititzen dugu wsl kanalizazio-sarrera eta komando-lerroko edozein argumentu.

Bilgarri hauekin zure Linux komando gogokoenak modu naturalago batean deitu ditzakezu aurrizkirik gehitu gabe wsl eta bideak nola bihurtzen diren kezkatu gabe:

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

Oinarrizko komandoen multzoa erakusten da hemen, baina Linux-eko edozein komandotarako shell bat sor dezakezu zerrendara gehituta. Kode hau gehitzen baduzu zure profila PowerShell, komando hauek PowerShell saio guztietan egongo dira eskuragarri, jatorrizko komandoak bezala!

Ezarpen lehenetsiak

Linux-en, ohikoa da saio-hasiera-profiletan aliasak eta/edo ingurune-aldagaiak definitzea, maiz erabiltzen diren komandoetarako parametro lehenetsiak ezarriz (adibidez, alias ls=ls -AFh edo export LESS=-i). Shell ez-interaktibo baten bidez proxy egitearen desabantailetako bat wsl.exe - profilak ez daudela kargatzen, beraz, aukera hauek ez daude erabilgarri lehenespenez (hau da. ls WSLn eta wsl ls portaera ezberdina izango da goian zehaztutako ezizenarekin).

PowerShellek eskaintzen du $PSDefaultParameterValues, parametro lehenetsiak definitzeko mekanismo estandarra, baina cmdletetarako eta funtzio aurreratuetarako soilik. Jakina, gure shelletatik funtzio aurreratuak egin ditzakegu, baina horrek alferrikako konplikazioak dakartza (adibidez, PowerShell-ek parametroen izen partzialak erlazionatzen ditu (adibidez, -a rekin erlazionatzen du -ArgumentList), izen partzialak argumentu gisa hartzen dituzten Linux komandoekin gatazkan egongo dena), eta balio lehenetsiak definitzeko sintaxia ez da egokiena izango (argumentu lehenetsiek gakoaren parametroaren izena eskatzen dute, ez komandoaren izena bakarrik) .

Hala ere, gure maskorretan aldaketa txiki batekin, antzeko eredu bat inplementa dezakegu $PSDefaultParameterValues, eta gaitu Linux komandoetarako aukera lehenetsiak!

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

Pasatzea $WslDefaultParameterValues komando-lerrora, parametroak bidaltzen ditugu wsl.exe. Jarraian, zure PowerShell profilean ezarpen lehenetsiak konfiguratzeko argibideak nola gehitu erakusten da. Orain egin dezakegu!

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

Parametroak ondoren modelatzen direnez $PSDefaultParameterValuesahal duzu erraza da horiek desgaitzea aldi baterako giltza instalatuz "Disabled" esanahian sartu $true. Hash taula bereizi baten abantaila gehigarri bat desgaitzeko gaitasuna da $WslDefaultParameterValues bereizita $PSDefaultParameterValues.

Argudioa osatzea

PowerShell-ek komandoa erabiliz argumentu-trailerrak erregistratzeko aukera ematen du Register-ArgumentCompleter. Bashek indartsua du automatikoki osatzeko tresna programagarriak. WSL-k bash-i deitzeko aukera ematen dizu PowerShell-etik. Gure PowerShell funtzioen bilgarrietarako argumentuen osaketak erregistratu eta osaketak sortzeko bash deitzen badugu, argumentu osoa bash-en zehaztasun berarekin lortuko dugu!

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

Kodea trinko samarra da bash-en barne-funtzio batzuk ulertu gabe, baina funtsean egiten duguna hau da:

  • Gure funtzio-bilgarri guztietarako argumentu-osatzaile bat erregistratzea zerrenda bat pasatuz $commands parametroan -CommandName egiteko Register-ArgumentCompleter.
  • Komando bakoitza bash-ek osatze automatikorako erabiltzen duen shell funtzioarekin mapatzen dugu (auto-osatze-zehaztapenak definitzeko, bash-ek erabiltzen du $F, laburdura complete -F <FUNCTION>).
  • PowerShell argudioak bihurtzea $wordToComplete, $commandAst и $cursorPosition bash-en osatze automatikoaren funtzioek espero duten formatuan, zehaztapenen arabera osaketa automatiko programagarria zartada.
  • Komando-lerro bat osatzen dugu transferitzeko wsl.exe, ingurunea behar bezala konfiguratuta dagoela ziurtatzen duena, osatze automatikoko funtzio egokia deitzen du eta emaitzak lerroz lerro moduan ateratzen ditu.
  • Orduan deitzen dugu wsl komando-lerroarekin, irteera lerro-bereizleen bidez bereizten dugu eta bakoitzarentzat sortzen dugu CompletionResults, ordenatuz eta bestela gaizki interpretatuko liratekeen zuriuneak eta parentesiak bezalako karaktereak ihes eginez.

Ondorioz, gure Linux komando-shellek bash-en osatze automatiko bera erabiliko dute! Adibidez:

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

Osatze automatiko bakoitzak aurreko argumentuaren balio espezifikoak ematen ditu, WSL-ko ostalari ezagunak bezalako konfigurazio datuak irakurriz!

<TAB> parametroak zeharkatuko ditu. <Ctrl + пробел> eskuragarri dauden aukera guztiak erakutsiko ditu.

Gainera, orain bash osatze automatikoa dugunez, Linux bideak automatikoki osa ditzakezu zuzenean PowerShell-en!

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

Bash osatze automatikoak emaitzarik ematen ez duen kasuetan, PowerShell sistemaren lehenetsitako Windows bideetara itzultzen da. Horrela, praktikan, bi bideak aldi berean erabil ditzakezu zure diskrezioan.

Ondorioa

PowerShell eta WSL erabiliz, Linux komandoak Windows-en integra ditzakegu jatorrizko aplikazio gisa. Ez dago Win32 konposizioak edo Linux utilitateak bilatu edo zure lan-fluxua eten behar Linux shell batera joanda. Besterik gabe instalatu WSL, konfiguratu PowerShell profila и zerrendatu inportatu nahi dituzun komandoak! Linux eta Windows komandoen parametroetarako eta fitxategi bideetarako osatze automatiko aberatsa gaur egun jatorrizko Windows komandoetan ere eskuragarri ez dagoen funtzionaltasuna da.

Goian deskribatutako iturburu-kode osoa, baita zure lan-fluxuan sartzeko jarraibide osagarriak ere eskuragarri daude Hemen.

Zein Linux komando iruditzen zaizkizu baliagarrienak? Zer beste gauza arrunt falta dira Windows-en lan egitean? Idatzi iruzkinetan edo GitHub-en!

Iturria: www.habr.com

Gehitu iruzkin berria