# PowerShell 7.x - قسمت دوم - آشنایی با Pipelineها

PowerShell برای نام‌گذاری Commandها، از ساختار verb-noun استفاده میکند. به عنوان مثال Get-Command, New-Service, Get-Help نمونه‌هایی از این Commandها در PowerShell هستند. لازم به ذکر است که در PowerShe

- Published: 2022-10-05
- Language: fa
- Tags: DNTips
- Canonical: https://sirwan.info/blog/fa/dntips-3414

---

> این نوشته نخستین بار در [دات‌نت تیپس](https://www.dntips.ir/post/3414) منتشر شده است.

<div class="postBody">PowerShell برای نام‌گذاری Commandها، از ساختار verb-noun استفاده میکند. به عنوان مثال Get-Command, New-Service, Get-Help نمونه‌هایی از این Commandها در PowerShell هستند. لازم به ذکر است که در PowerShell، منظور از cmdlet یا Command let، همان Commandهای native در PowerShell هستند؛ نه Commandهای عمومی مانند dir, cd, ipconfig و غیره. به عنوان مثال از Get-Help برای نمایش مستندات یک cmdlet میتوان استفاده کرد و دقیقاً مشابه man page در لینوکس است.<div> <br/> </div> <div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Get-Help Get-Command</pre>
 </div> </div> <div>با فلگ Online میتوان مستندات cmdlet موردنظر را درون مرورگر مشاهده کرد:<br/> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Get-Help Get-Command -Online</pre>
 </div>
برای بیشتر cmdletها میتوانیم فیلتر نیز اعمال کنیم. به عنوان مثال با دستور زیر میتوان لیست تمام processهای سیستم را که به واژه‌ی Process ختم میشوند، مشاهده کنیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Get-Command -Name '*Process'</pre>
 </div>
خروجی دستور فوق، یک جدول به صورت زیر خواهد بود:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">CommandType     Name                                               Version    Sour&#10;                                                                              ce&#10;-----------     ----                                               -------    ----&#10;Cmdlet          Debug-Process                                      7.0.0.0    Mic…&#10;Cmdlet          Enter-PSHostProcess                                7.2.6.500  Mic…&#10;Cmdlet          Exit-PSHostProcess                                 7.2.6.500  Mic…&#10;Cmdlet          Get-Process                                        7.0.0.0    Mic…&#10;Cmdlet          Start-Process                                      7.0.0.0    Mic…&#10;Cmdlet          Stop-Process                                       7.0.0.0    Mic…&#10;Cmdlet          Wait-Process                                       7.0.0.0    Mic…&#10;Application     mysqltest_safe_process                             0.0.0.0</pre>
 </div> </div> <div> <b> <br/> </b> </div> <div> <b>Pipeline</b> </div> <div> <div>توسط Pipeline میتوان خروجی یک command را به عنوان ورودی یک command دیگر ارسال کرد. در دیگر زبان‌های اسکریپتی مانند bash یا batch (در ویندوز) چیزی که به command بعدی ارسال میشود، در واقع یک text است:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">ls -l | grep "\.pdf$"</pre>
 </div> </div> <div>در مثال فوق، خروجی برنامه ls -1 را به ورودی برنامه grep ارسال کرده‌ایم. در حالت عادی، خروجی دستورات به standard output یا به طور خلاصه stout ارسال میشوند. توسط pipe یا pipeline میتوانیم خروجی متنی را به اصطلاح redirect کنیم و به کامندهای بعدی به صورت یک زنجیره ارسال کنیم. اما در PowerShell این objectها هستند که ارسال (pipe) میشوند. به عنوان مثال میتوانیم با کمک Pipelineها، خروجی مثال قبل را محدود به نمایش ستون‌های دلخواهی کنیم. به عبارتی تنها ستون‌های Name و CommandType را در خروجی نمایش دهیم:</div> </div> <div> <div align="left" dir="ltr" style="direction: ltr;"> </div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Get-Command -Name '*Process' | Select-Object Name,CommandType</pre>
 </div>
همچنین میتوانیم خروجی را با کمک Sort-Object مرتب کنیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Get-Command -Name '*Process' | Select-Object Name,CommandType | Sort-Object Name -Descending</pre>
 </div>
یا اینکه خروجی را محدود به نمایش ۳ آیتم کنیم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Get-Command -Name '*Process' | Select-Object Name,CommandType -First 3 | Sort-Object Name -Descending&#10;&#10;Name                CommandType&#10;----                -----------&#10;Exit-PSHostProcess       Cmdlet&#10;Enter-PSHostProcess      Cmdlet&#10;Debug-Process            Cmdlet</pre>
 </div> </div> <div>همچنین میتوانیم از Where-Object برای اعمال شرط نیز استفاده کنیم. به عنوان مثال، در ادامه لیست ۵ پروسس سیستم را که مقدار CPU بیشتر از 1.24، در اختیار دارند نمایش داده‌ایم:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /&gt; Get-Process | Where-Object CPU -gt 1.24 | Sort-Object WorkingSet -Descending | Select-Object -First 5</pre>
 </div> </div> <div> <b> <br/> </b> </div> <div> <b>Pipelineها چطور کار میکنند؟</b> </div> <div> <div>در PowerShell در واقع stdinی وجود ندارد که shell از آن استفاده کند؛ در نتیجه PowerShell باید بداند خروجی cmdlet قبلی را به کدام پراپرتی از cmdlet بعدی در pipeline ارسال کند:</div> <div> <br/> </div> <div> <p style="margin-left: auto; margin-right: auto;"> <img src="/img/dntips/41a7204ff71da9e965ad.png" style="display: block; margin-left: auto; margin-right: auto; cursor: default;"/> </p> </div> <div> <div> <br/> </div> <div>PowerShell از مکانیزمی تحت عنوان pipeline binding برای انجام این نگاشت استفاده میکند. دو روش برای انجام این binding وجود دارد:</div> <div> <ul> <li>ByValue</li> <li>ByPropertyName  </li> </ul> </div> <div> <div> <div>در نظر داشته باشید که هر کدام از روش‌های فوق توسط کسی که cmdlet موردنظر را پیاده‌سازی خواهد کرد میتواند پشتیبانی شود. برای درک بهتر این مکانیزم دستور زیر را در نظر بگیرید:</div> </div> </div> <div> <div align="left" dir="ltr" style="direction: ltr;"> </div> </div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Get-Process Slack | Stop-Process</pre>
 </div> <div>قبل از هر چیزی باید بدانیم خروجی cmdlet اول یعنی Get-Process چه چیزی است. اینکار را میتوانیم توسط دستور زیر انجام دهیم:<br/> </div> </div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Get-Process | Get-Member</pre>
 </div>
دستور فوق یک لیست از خواص Get-Process خواهد بود و خط اول این خروجی دقیقاً تایپی است که Get-Process برمیگرداند:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">TypeName: System.Diagnostics.Process</pre>
 </div>
بنابراین این تایپی است که به عنوان ورودی، به Stop-Process درون pipeline ارسال میشود. در ادامه توسط دستور Get-Help Stop-Process -Full لیست پارامترهایی را که Stop-Process دریافت میکند، لیست خواهیم کرد:</div> <div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /Users/sirwanafifi&gt; Get-Help Stop-Process -Full&#10;&#10;NAME&#10;    Stop-Process&#10;&#10;SYNTAX&#10;    Stop-Process [-Id] &lt;int[]&gt; [-PassThru] [-Force] [-WhatIf] [-Confirm] [&lt;CommonParameters&gt;]&#10;&#10;    Stop-Process -Name &lt;string[]&gt; [-PassThru] [-Force] [-WhatIf] [-Confirm] [&lt;CommonParameters&gt;]&#10;&#10;    Stop-Process [-InputObject] &lt;Process[]&gt; [-PassThru] [-Force] [-WhatIf] [-Confirm] [&lt;CommonParameters&gt;]&#10;&#10;&#10;PARAMETERS&#10;    -Confirm&#10;&#10;        Required?                    false&#10;        Position?                    Named&#10;        Accept pipeline input?       false&#10;        Parameter set name           (All)&#10;        Aliases                      cf&#10;        Dynamic?                     false&#10;        Accept wildcard characters?  false&#10;&#10;    -Force&#10;&#10;        Required?                    false&#10;        Position?                    Named&#10;        Accept pipeline input?       false&#10;        Parameter set name           (All)&#10;        Aliases                      None&#10;        Dynamic?                     false&#10;        Accept wildcard characters?  false&#10;&#10;    -Id &lt;int[]&gt;&#10;&#10;        Required?                    true&#10;        Position?                    0&#10;        Accept pipeline input?       true (ByPropertyName)&#10;        Parameter set name           Id&#10;        Aliases                      None&#10;        Dynamic?                     false&#10;        Accept wildcard characters?  false&#10;&#10;    -InputObject &lt;Process[]&gt;&#10;&#10;        Required?                    true&#10;        Position?                    0&#10;        Accept pipeline input?       true (ByValue)&#10;        Parameter set name           InputObject&#10;        Aliases                      None&#10;        Dynamic?                     false&#10;        Accept wildcard characters?  false&#10;&#10;    -Name &lt;string[]&gt;&#10;&#10;        Required?                    true&#10;        Position?                    Named&#10;        Accept pipeline input?       true (ByPropertyName)&#10;        Parameter set name           Name&#10;        Aliases                      ProcessName&#10;        Dynamic?                     false&#10;        Accept wildcard characters?  false&#10;&#10;    -PassThru&#10;&#10;        Required?                    false&#10;        Position?                    Named&#10;        Accept pipeline input?       false&#10;        Parameter set name           (All)&#10;        Aliases                      None&#10;        Dynamic?                     false&#10;        Accept wildcard characters?  false&#10;&#10;    -WhatIf&#10;&#10;        Required?                    false&#10;        Position?                    Named&#10;        Accept pipeline input?       false&#10;        Parameter set name           (All)&#10;        Aliases                      wi&#10;        Dynamic?                     false&#10;        Accept wildcard characters?  false&#10;&#10;    &lt;CommonParameters&gt;&#10;        This cmdlet supports the common parameters: Verbose, Debug,&#10;        ErrorAction, ErrorVariable, WarningAction, WarningVariable,&#10;        OutBuffer, PipelineVariable, and OutVariable. For more information, see&#10;        about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216).&#10;&#10;&#10;INPUTS&#10;    System.Int32[]&#10;    System.String[]&#10;    System.Diagnostics.Process[]&#10;&#10;&#10;OUTPUTS&#10;    System.Diagnostics.Process&#10;&#10;&#10;ALIASES&#10;    spps&#10;&#10;&#10;REMARKS&#10;    Get-Help cannot find the Help files for this cmdlet on this computer. It is displaying only partial help.&#10;        -- To download and install Help files for the module that includes this cmdlet, use Update-Help.&#10;        -- To view the Help topic for this cmdlet online, type: "Get-Help Stop-Process -Online" or&#10;           go to https://go.microsoft.com/fwlink/?LinkID=2097058.</pre>
 </div> </div> <div>از پارامترهای فوق تنها Id, Name و InputObject هستند که خاصیت Accept pipeline inputشان به true تنظیم شده‌است. همانطور که مشاهده میکنید InputObject از نوع ByValue است و Id و Name نیز از نوع ByPropertyName هستند:</div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">-Id &lt;int[]&gt;&#10;&#10;    Required?                    true&#10;    Position?                    0&#10;    Accept pipeline input?       true (ByPropertyName)&#10;    Parameter set name           Id&#10;    Aliases                      None&#10;    Dynamic?                     false&#10;    Accept wildcard characters?  false&#10;&#10;-InputObject &lt;Process[]&gt;&#10;&#10;    Required?                    true&#10;    Position?                    0&#10;    Accept pipeline input?       true (ByValue)&#10;    Parameter set name           InputObject&#10;    Aliases                      None&#10;    Dynamic?                     false&#10;    Accept wildcard characters?  false&#10;&#10;-Name &lt;string[]&gt;&#10;&#10;    Required?                    true&#10;    Position?                    Named&#10;    Accept pipeline input?       true (ByPropertyName)&#10;    Parameter set name           Name&#10;    Aliases                      ProcessName&#10;    Dynamic?                     false&#10;    Accept wildcard characters?  false</pre>
 </div> <div>نوع ورودی این پارامتر نیز یک آرایه از Processها میباشد. بنابراین در اینجا ByValue به این معنا است که اگر مقدار pipe شده از نوع Process بود، پراپرتی InputObject مقداردهی میشود. برای حالت ByPropertyName دستور زیر را در نظر بگیرید:</div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">"Slack" | Stop-Process</pre>
 </div> <div>با اجرای دستور فوق خطای زیر را دریافت خواهیم کرد:</div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">Stop-Process: The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.</pre>
 </div> <div>علت آن نیز مشخص است؛ چون هیچ پراپرتی از نوع ByValue که ورودی string یا آرایه‌ایی از stringها را دریافت کند برای Stop-Process وجود ندارد، بنابراین pipeline binding اتفاق نخواهد افتاد. برای درک بهتر موضوع، یک شیء تستی ایجاد خواهیم کرد که شامل یک پراپرتی Name با مقدار Slack است؛ سپس شیء جدید را به Stop-Process ارسال میکنیم:</div> <div align="left" dir="ltr" style="direction: ltr;">
<pre language="CSharp" name="code">PS /Users/sirwanafifi&gt; $newObject = [pscustomobject]@{ Name = "Slack" }&#10;PS /Users/sirwanafifi&gt; $newObject | Stop-Process</pre>
 </div> <div>با اجرای دستور فوق، پراسس موردنظر stop خواهد شد.</div> <div>لازم به ذکر است که اگر یک پارامتر، هم ByValue و هم ByPropertyName باشد، PowerShell ابتدا سعی میکند ByValue را امتحان کند و اگر با شکست مواجه شد از ByPropertyName استفاده خواهد کرد.</div> </div> </div></div>
