PowerShell اور WSL کا استعمال کرتے ہوئے لینکس کمانڈز کو ونڈوز میں ضم کرنا

ونڈوز ڈویلپرز کا ایک عام سوال: "ابھی تک کیوں نہیں ہے۔ <ВСТАВЬТЕ ТУТ ЛЮБИМУЮ КОМАНДУ LINUX>؟ چاہے یہ ایک طاقتور سوائپ ہو۔ less یا واقف ٹولز grep یا sed، ونڈوز ڈویلپر اپنے روزمرہ کے کام میں ان کمانڈز تک آسان رسائی چاہتے ہیں۔

ونڈوز سب سسٹم برائے لینکس (WSL) اس سلسلے میں ایک بہت بڑا قدم اٹھایا ہے۔ یہ آپ کو ونڈوز سے لینکس کمانڈز کو پراکسی کرکے کال کرنے کی اجازت دیتا ہے۔ wsl.exe (جیسے۔ wsl ls)۔ اگرچہ یہ ایک نمایاں بہتری ہے، لیکن یہ آپشن بہت سے نقصانات سے دوچار ہے۔

  • ہر جگہ اضافہ wsl تکلیف دہ اور غیر فطری.
  • دلائل میں ونڈوز کے راستے ہمیشہ کام نہیں کرتے کیونکہ بیک سلیش کو ڈائرکٹری الگ کرنے والوں کے بجائے فرار ہونے والے کرداروں سے تعبیر کیا جاتا ہے۔
  • آرگیومینٹس میں ونڈوز پاتھ کا WSL میں متعلقہ ماؤنٹ پوائنٹ میں ترجمہ نہیں کیا جاتا ہے۔
  • عرفی ناموں اور ماحولیاتی متغیرات کے ساتھ WSL پروفائلز میں پہلے سے طے شدہ ترتیبات کا احترام نہیں کیا جاتا ہے۔
  • لینکس پاتھ کی تکمیل تعاون یافتہ نہیں ہے۔
  • کمانڈ کی تکمیل تعاون یافتہ نہیں ہے۔
  • دلیل کی تکمیل معاون نہیں ہے۔

نتیجے کے طور پر، لینکس کمانڈز کے ساتھ ونڈوز کے تحت دوسرے درجے کے شہریوں کی طرح سلوک کیا جاتا ہے — اور مقامی کمانڈز کے مقابلے میں استعمال کرنا زیادہ مشکل ہے۔ ان کے حقوق کو برابر کرنے کے لیے درج مسائل کو حل کرنا ضروری ہے۔

پاور شیل فنکشن ریپرز

پاور شیل فنکشن ریپرز کے ساتھ، ہم کمانڈ کی تکمیل کو شامل کر سکتے ہیں اور سابقے کی ضرورت کو ختم کر سکتے ہیں۔ wsl، ونڈوز کے راستوں کا WSL پاتھوں میں ترجمہ کرنا۔ شیل کے لئے بنیادی ضروریات:

  • ہر لینکس کمانڈ کے لیے ایک ہی نام کا ایک فنکشن ریپر ہونا چاہیے۔
  • شیل کو دلائل کے طور پر منظور شدہ ونڈوز کے راستوں کو پہچاننا چاہیے اور انہیں WSL پاتھوں میں تبدیل کرنا چاہیے۔
  • شیل کو کال کرنا چاہئے wsl کسی بھی پائپ لائن ان پٹ پر مناسب لینکس کمانڈ کے ساتھ اور فنکشن میں پاس کردہ کمانڈ لائن آرگومنٹس کو پاس کرنا۔

چونکہ یہ پیٹرن کسی بھی کمانڈ پر لاگو کیا جا سکتا ہے، ہم ان ریپرز کی تعریف کو خلاصہ کر سکتے ہیں اور انہیں درآمد کرنے کے لیے کمانڈز کی فہرست سے متحرک طور پر تخلیق کر سکتے ہیں۔

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

RЎRїReSЃRѕRє $command درآمدی احکامات کی وضاحت کرتا ہے۔ اس کے بعد ہم کمانڈ کا استعمال کرتے ہوئے ان میں سے ہر ایک کے لیے متحرک طور پر ایک فنکشن ریپر تیار کرتے ہیں۔ Invoke-Expression (پہلے کسی ایسے عرفی نام کو ہٹا کر جو فنکشن سے متصادم ہو)۔

فنکشن کمانڈ لائن آرگیومینٹس پر اعادہ کرتا ہے، کمانڈز کا استعمال کرتے ہوئے ونڈوز کے راستوں کا تعین کرتا ہے۔ Split-Path и Test-Pathاور پھر ان راستوں کو WSL پاتھوں میں تبدیل کرتا ہے۔ ہم ایک مددگار فنکشن کے ذریعے راستے چلاتے ہیں۔ Format-WslArgumentجس کی وضاحت ہم بعد میں کریں گے۔ یہ خاص حروف جیسے خالی جگہوں اور قوسین سے بچ جاتا ہے جن کی دوسری صورت میں غلط تشریح کی جائے گی۔

آخر میں، ہم پہنچاتے ہیں wsl پائپ لائن ان پٹ اور کسی بھی کمانڈ لائن دلائل۔

ان ریپرز کے ذریعے آپ اپنی پسندیدہ لینکس کمانڈز کو بغیر کسی سابقہ ​​کے شامل کیے زیادہ قدرتی انداز میں کال کر سکتے ہیں۔ wsl اور اس بات کی فکر کیے بغیر کہ راستے کیسے تبدیل ہوتے ہیں:

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

کمانڈز کا بنیادی سیٹ یہاں دکھایا گیا ہے، لیکن آپ کسی بھی لینکس کمانڈ کے لیے اسے فہرست میں شامل کرکے شیل بنا سکتے ہیں۔ اگر آپ اس کوڈ کو اپنے پروفائل پاور شیل، یہ کمانڈز آپ کے لیے ہر پاور شیل سیشن میں دستیاب ہوں گی، بالکل مقامی کمانڈز کی طرح!

پہلے سے طے شدہ ترتیبات

لینکس میں، لاگ ان پروفائلز میں عرفی نام اور/یا ماحولیاتی متغیرات کی وضاحت کرنا، اکثر استعمال ہونے والی کمانڈز کے لیے ڈیفالٹ پیرامیٹرز ترتیب دینا عام ہے (مثال کے طور پر، alias ls=ls -AFh یا export LESS=-i)۔ غیر انٹرایکٹو شیل کے ذریعے پراکسی کرنے کے نقصانات میں سے ایک wsl.exe - کہ پروفائلز لوڈ نہیں ہیں، اس لیے یہ اختیارات بطور ڈیفالٹ دستیاب نہیں ہیں (یعنی ls WSL میں اور wsl ls اوپر بیان کردہ عرف کے ساتھ مختلف برتاؤ کرے گا)۔

پاور شیل فراہم کرتا ہے۔ $PSDefaultParameterValues، پہلے سے طے شدہ پیرامیٹرز کی وضاحت کے لیے ایک معیاری طریقہ کار، لیکن صرف cmdlets اور جدید فنکشنز کے لیے۔ بلاشبہ، ہم اپنے خولوں سے جدید فنکشنز بنا سکتے ہیں، لیکن اس سے غیر ضروری پیچیدگیاں پیدا ہوتی ہیں (مثال کے طور پر، پاور شیل جزوی پیرامیٹر کے ناموں کو جوڑتا ہے (مثال کے طور پر، -a کے ساتھ تعلق رکھتا ہے -ArgumentList)، جو لینکس کمانڈز سے متصادم ہوگا جو جزوی ناموں کو بطور دلیل لیتے ہیں)، اور ڈیفالٹ اقدار کی وضاحت کے لیے نحو سب سے زیادہ مناسب نہیں ہوگا (پہلے سے طے شدہ دلائل کلید میں پیرامیٹر کے نام کی ضرورت ہوتی ہے، نہ کہ صرف کمانڈ کا نام) .

تاہم، اپنے خولوں میں تھوڑی سی ترمیم کے ساتھ، ہم اس سے ملتا جلتا ماڈل نافذ کر سکتے ہیں۔ $PSDefaultParameterValues، اور لینکس کمانڈز کے لیے پہلے سے طے شدہ اختیارات کو فعال کریں!

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.

دلیل کی تکمیل

پاور شیل آپ کو کمانڈ کا استعمال کرتے ہوئے دلیل کے ٹریلرز کو رجسٹر کرنے کی اجازت دیتا ہے۔ Register-ArgumentCompleter. باش طاقتور ہے قابل پروگرام خودکار تکمیل کے اوزار. WSL آپ کو پاور شیل سے bash کال کرنے کی اجازت دیتا ہے۔ اگر ہم اپنے پاور شیل فنکشن ریپرز کے لیے دلیل کی تکمیل کو رجسٹر کر سکتے ہیں اور تکمیلات کو پیدا کرنے کے لیے کال 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]
    }
}

باش کے کچھ اندرونی افعال کو سمجھے بغیر کوڈ تھوڑا سا گھنا ہے، لیکن بنیادی طور پر ہم کیا کرتے ہیں یہ ہے:

  • ایک لسٹ پاس کر کے ہمارے تمام فنکشن ریپرز کے لیے آرگومنٹ مکمل کرنے والے کو رجسٹر کرنا $commands پیرامیٹر میں -CommandName لیے Register-ArgumentCompleter.
  • ہم ہر کمانڈ کو شیل فنکشن میں نقشہ بناتے ہیں جو bash خودکار تکمیل کے لیے استعمال کرتا ہے (خود تکمیل کی وضاحتیں بیان کرنے کے لیے، bash استعمال کرتا ہے $Fکے لیے مخفف complete -F <FUNCTION>).
  • PowerShell دلائل کو تبدیل کرنا $wordToComplete, $commandAst и $cursorPosition تصریحات کے مطابق bash کے خودکار تکمیل کے افعال سے متوقع فارمیٹ میں قابل پروگرام خودکار تکمیل bash
  • ہم منتقل کرنے کے لیے ایک کمانڈ لائن مرتب کرتے ہیں۔ wsl.exe، جو اس بات کو یقینی بناتا ہے کہ ماحول درست طریقے سے ترتیب دیا گیا ہے، مناسب آٹو تکمیل فنکشن کو کال کرتا ہے، اور نتائج کو لائن بہ لائن انداز میں ظاہر کرتا ہے۔
  • پھر ہم کال کرتے ہیں۔ wsl کمانڈ لائن کے ساتھ، ہم آؤٹ پٹ کو لائن سیپریٹرز کے ذریعے الگ کرتے ہیں اور ہر ایک کے لیے پیدا کرتے ہیں۔ CompletionResults، ان کو چھانٹنا اور حروف سے بچنا جیسے خالی جگہیں اور قوسین جن کی دوسری صورت میں غلط تشریح کی جائے گی۔

نتیجے کے طور پر، ہمارے لینکس کمانڈ شیل بالکل وہی خود کار تکمیل استعمال کریں گے جیسے bash! مثال کے طور پر:

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

ہر خودکار تکمیل پچھلی دلیل کے لیے مخصوص اقدار فراہم کرتی ہے، ترتیب کے ڈیٹا کو پڑھتا ہے جیسے کہ WSL سے معروف میزبان!

<TAB> پیرامیٹرز کے ذریعے چکر لگائے گا۔ <Ctrl + пробел> تمام دستیاب اختیارات دکھائے گا۔

اس کے علاوہ، چونکہ اب ہمارے پاس bash آٹوکمپلیشن ہے، آپ لینکس کے راستوں کو براہ راست PowerShell میں خود بخود مکمل کر سکتے ہیں!

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

ایسی صورتوں میں جہاں bash خودکار تکمیل کوئی نتیجہ نہیں دیتی، PowerShell سسٹم کے پہلے سے طے شدہ ونڈوز کے راستوں پر واپس آجاتا ہے۔ اس طرح، عملی طور پر، آپ اپنی صوابدید پر بیک وقت دونوں راستے استعمال کر سکتے ہیں۔

حاصل يہ ہوا

PowerShell اور WSL کا استعمال کرتے ہوئے، ہم لینکس کمانڈز کو ونڈوز میں مقامی ایپلی کیشنز کے طور پر ضم کر سکتے ہیں۔ Win32 بلڈز یا لینکس یوٹیلیٹیز کو تلاش کرنے یا لینکس شیل پر جا کر آپ کے ورک فلو میں خلل ڈالنے کی ضرورت نہیں ہے۔ بس WSL انسٹال کریں۔، ترتیب دیں۔ پاور شیل پروفائل и ان کمانڈز کی فہرست بنائیں جنہیں آپ درآمد کرنا چاہتے ہیں۔! لینکس اور ونڈوز کمانڈ پیرامیٹرز اور فائل پاتھز کے لیے بھرپور خودکار تکمیل وہ فعالیت ہے جو آج مقامی ونڈوز کمانڈز میں بھی دستیاب نہیں ہے۔

اوپر بیان کردہ مکمل سورس کوڈ، نیز اسے آپ کے ورک فلو میں شامل کرنے کے لیے اضافی رہنما خطوط دستیاب ہیں۔ یہاں.

آپ کو کون سی لینکس کمانڈز سب سے زیادہ کارآمد لگتی ہیں؟ ونڈوز میں کام کرتے وقت کون سی دوسری عام چیزیں غائب ہیں؟ کمنٹس میں لکھیں یا GitHub پر!

ماخذ: www.habr.com

نیا تبصرہ شامل کریں