PowerShell ، تفريغ تجربتي

مقدمة


هذه المقالة موجهة إلى أولئك الذين أصبحوا بالفعل على دراية بأساسيات PowerShell ، ويقومون بتشغيل بعض البرامج النصية باستخدام stackexchange ، وربما يكون لديهم ملف نصي خاص بهم مع مقتطفات متنوعة لتسهيل العمل اليومي. الغرض من كتابتها هو تقليل الانتروبيا وزيادة قابلية كود PowerShell وقابليته للصيانة المستخدمة في شركتك ، ونتيجة لذلك ، زيادة إنتاجية المسؤول الذي يعمل معها.


kdpv


في مكان عملي السابق ، نظرًا لخصائص المهام والعيوب في العالم ، ضخت إلى حد كبير مهارة العمل مع PowerShell. بعد تغيير العمل ، انخفضت الأحمال من هذا النوع بشكل كبير وبدأ كل شيء كان قاب قوسين أو أدنى من الأصابع بالغرق أعمق وأعمق تحت تجربة حل المشاكل الجديدة مع التقنيات الجديدة. من هذا ، تدعي هذه المقالة أنها فقط ما تعلنه ، حيث تكشف عن قائمة بالموضوعات التي ، من وجهة نظري ، ستكون مفيدة لي بنفسي قبل حوالي 7 سنوات ، عندما كانت معرفتي بهذه الأداة قد بدأت للتو.


إذا كنت لا تفهم سبب كون PowerShell عبارة عن غلاف موجه للكائنات ، وما نوع المكافآت التي تأتي منها والسبب في كونها ضرورية على الإطلاق ، فإنني أنصحك ، على الرغم من الكارهين ، كتاب جيد يقدم بسرعة جوهر هذه البيئة - Andrei Popov ، مقدمة إلى Windows PowerShell . نعم ، إنه يتعلق بالإصدار القديم من PS ، نعم ، اكتسبت اللغة بعض الامتدادات والتحسينات ، لكن هذا الكتاب جيد لأنه عندما يصف المرحلة المبكرة من تطور هذه البيئة ، فإنه يركز دون قصد على الأشياء الأساسية فقط. أعتقد أن السكر النحوي الذي غمرته البيئة ، تدركه بسرعة دون أن تفهم كيف يعمل المفهوم. ستأخذك قراءة هذا الكتاب إلى أمسيات قليلة ، عد بعد القراءة.


بوبوف


الكتاب متاح أيضًا على موقع المؤلف ، على الرغم من أنني لست متأكدًا من مدى ترخيص هذا الاستخدام: https://andpop.ru/courses/winscript/books/posh_popov.pdf


أدلة النمط


يعد تصميم البرامج النصية وفقًا لإرشادات التصميم ممارسة جيدة في جميع حالات تطبيقها ، ولا يمكن أن يكون هناك رأيين. اهتمت بعض الأنظمة الإيكولوجية بهذا الأمر على مستوى التوليف الأصلي ، فإن pep8 في مجتمع Python وتذهب إلى الأبد في Golang. هذه أدوات لتوفير الوقت لا تقدر بثمن ، والتي للأسف ، غائبة في حزمة PowerShell القياسية ، وبالتالي تنقل المشكلة إلى رأسنا. في الوقت الحالي ، تتمثل الطريقة الوحيدة لحل مشكلة تنسيق الكود الموحد في تطوير ردود الفعل عن طريق كتابة الكود مرارًا وتلبية أدلة النمط (في الواقع ، لا).


أدلة النمط بسبب عدم اعتماد Microsoft رسميًا ووصفها بالتفصيل ، وُلدت في المجتمع أثناء وقت PowerShell v3 ومنذ ذلك الحين تم تطويرها في شكل مفتوح على github: PowerShellPracticeAndStyle . هذا مستودع جدير بالملاحظة لأي شخص استخدم زر "حفظ" في PowerShell.


إذا حاولت الضغط ، فربما ينخفض ​​الأمر إلى النقاط التالية:


  • يستخدم PowerShell PascalCase لتسمية المتغيرات ، أوامر cmdlets ، أسماء الوحدات ، وكل شيء باستثناء المشغلات ؛
  • -match اللغات مثل if ، switch ، break ، process ، -match مكتوبة بأحرف صغيرة جدًا ؛
  • يتم وضع الأقواس المعقوفة في الطريقة الحقيقية الوحيدة ، وإلا تسمى أيضًا أسلوب Kernigan و Richie ، والتي تقود قصته من كتاب The C Programming Language ؛
  • لا تستخدم الأسماء المستعارة في أي مكان آخر غير جلسة وحدة التحكم التفاعلية ، لا تكتب أي ps | ? processname -eq firefox | %{$ws=0}{$ws+=$_.workingset}{$ws/1MB} ملف البرنامج النصي ps | ? processname -eq firefox | %{$ws=0}{$ws+=$_.workingset}{$ws/1MB} ps | ? processname -eq firefox | %{$ws=0}{$ws+=$_.workingset}{$ws/1MB} ps | ? processname -eq firefox | %{$ws=0}{$ws+=$_.workingset}{$ws/1MB} ؛
  • أشر إلى أسماء المعلمات الصريحة ، وقد يتغير سلوك أوامر cmdlets وتوقيعها ، بالإضافة إلى أن هذا يضيف سياقًا إلى شخص غير معتاد على أمر cmdlet معين ؛
  • صمم معلمات استدعاء البرامج النصية ، ولا تكتب وظيفة داخل البرنامج النصي ، بينما يستدعي السطر الأخير هذه الوظيفة مع الحاجة إلى تغيير قيم المتغيرات العامة بدلاً من تحديد المعلمات ؛
  • حدد [CmdletBinding ()] - هذا سيمنح -Verbose cmdlet -Verbose و -Debug والعديد من الميزات المفيدة الأخرى . على الرغم من الموقف الثابت لبعض الأصوليين في المجتمع ، إلا أنني لست مؤيدًا للإشارة إلى هذه السمة في وظائف ومرشحات مضمنة بسيطة تتكون من عدة أسطر حرفية ؛
  • اكتب تعليمات قائمة على التعليقات: جملة واحدة ، رابط تذكرة ، مثال للاتصال ؛
  • حدد الإصدار المطلوب من PowerShell في قسم #requires ؛
  • استخدم Set-StrictMode -Version Latest ، سيساعدك ذلك على تجنب المشكلات الموضحة أدناه ؛
  • معالجة الأخطاء
  • لا تتعجل لإعادة كتابة كل شيء في PowerShell. PowerShell هو في المقام الأول قذيفة والثنائيات الدعوة هي مهمتها المباشرة. لا حرج في استخدام robocopy في نص برمجي ، بالطبع ليس rsync ، لكنه أيضًا جيد جدًا.

تعليق على أساس المساعدة


فيما يلي مثال عن كيفية الحصول على مساعدة البرنامج النصي. يقوم البرنامج النصي بإطار الصورة التي تصل بها إلى مربع ويقوم بتغيير الحجم ، وأعتقد أن لديك مهمة إنشاء تجسيدات للمستخدمين (باستثناء ربما التناوب وفقًا لبيانات exif). يوجد مثال للاستخدام في قسم. .EXAMPLE ، .EXAMPLE . نظرًا لحقيقة أن PowerShell يتم تشغيله بواسطة وقت تشغيل اللغة العامة ، مثل لغات dotnet الأخرى ، فإن لديه القدرة على استخدام القوة الكاملة لمكتبات dotnet:


 <# .SYNOPSIS Resize-Image resizes an image file .DESCRIPTION This function uses the native .NET API to crop a square and resize an image file .PARAMETER InputFile Specify the path to the image .PARAMETER OutputFile Specify the path to the resized image .PARAMETER SquareHeight Define the size of the side of the square of the cropped image. .PARAMETER Quality Jpeg compression ratio .EXAMPLE Resize the image to a specific size: .\Resize-Image.ps1 -InputFile "C:\userpic.jpg" -OutputFile "C:\userpic-400.jpg"-SquareHeight 400 #> # requires -version 3 [CmdletBinding()] Param( [Parameter( Mandatory )] [string]$InputFile, [Parameter( Mandatory )] [string]$OutputFile, [Parameter( Mandatory )] [int32]$SquareHeight, [ValidateRange( 1, 100 )] [int]$Quality = 85 ) # Add System.Drawing assembly Add-Type -AssemblyName System.Drawing # Open image file $Image = [System.Drawing.Image]::FromFile( $InputFile ) # Calculate the offset for centering the image $SquareSide = if ( $Image.Height -lt $Image.Width ) { $Image.Height $Offset = 0 } else { $Image.Width $Offset = ( $Image.Height - $Image.Width ) / 2 } # Create empty square canvas for the new image $SquareImage = New-Object System.Drawing.Bitmap( $SquareSide, $SquareSide ) $SquareImage.SetResolution( $Image.HorizontalResolution, $Image.VerticalResolution ) # Draw new image on the empty canvas $Canvas = [System.Drawing.Graphics]::FromImage( $SquareImage ) $Canvas.DrawImage( $Image, 0, -$Offset ) # Resize image $ResultImage = New-Object System.Drawing.Bitmap( $SquareHeight, $SquareHeight ) $Canvas = [System.Drawing.Graphics]::FromImage( $ResultImage ) $Canvas.DrawImage( $SquareImage, 0, 0, $SquareHeight, $SquareHeight ) $ImageCodecInfo = [System.Drawing.Imaging.ImageCodecInfo]::GetImageEncoders() | Where-Object MimeType -eq 'image/jpeg' # https://msdn.microsoft.com/ru-ru/library/hwkztaft(v=vs.110).aspx $EncoderQuality = [System.Drawing.Imaging.Encoder]::Quality $EncoderParameters = New-Object System.Drawing.Imaging.EncoderParameters( 1 ) $EncoderParameters.Param[0] = New-Object System.Drawing.Imaging.EncoderParameter( $EncoderQuality, $Quality ) # Save the image $ResultImage.Save( $OutputFile, $ImageCodecInfo, $EncoderParameters ) 

يبدأ النص أعلاه بتعليق متعدد الأسطر <# ... #> ، إذا جاء هذا التعليق أولاً واحتوى على كلمات رئيسية معينة ، فسيقوم PowerShell تلقائيًا بإنشاء مساعدة للبرنامج النصي. يسمى هذا النوع من المساعدة حرفيًا - المساعدة بناءً على التعليق :


مساعدة


علاوة على ذلك ، عند استدعاء البرنامج النصي ، ستعمل تلميحات الأدوات للمعلمات ، سواء أكانت وحدة التحكم في PowerShell أو محرر التعليمات البرمجية:


مساعدة مضمنة


مرة أخرى ، استرعي الانتباه إلى حقيقة أنه لا ينبغي إهمالها. إذا كنت لا تعرف ما تكتبه هناك ، فاكتب شيئًا ، واذهب إلى البرودة ، وعند عودتك سيكون لديك بالتأكيد فهمًا لما يجب تغييره في الكتابة. إنه يعمل. لا تملأ جميع الكلمات الرئيسية بشكل متعصب ، فقد تم تصميم PowerShell ليكون مستندًا ذاتيًا ، وإذا أعطيت أسماء ذات معنى وكاملة للمعلمات ، .SYNOPIS قصيرة في قسم .SYNOPIS ومثال واحد يكفي.


وضع صارم


يحتوي PowerShell ، مثل العديد من لغات البرمجة النصية الأخرى ، على كتابة ديناميكية. يحتوي هذا النوع من الكتابة على العديد من المؤيدين: كتابة منطق رفيع المستوى بسيط ولكنه قوي مسألة تستغرق بضع دقائق ، ولكن عندما يبدأ قرارك في الوصول إلى ألف سطر ، ستواجه بالتأكيد هشاشة هذا النهج.


سيؤدي إخراج الأنواع تلقائيًا دائمًا في مرحلة الاختبار ، والتي شكلت صفيفًا في المكان الذي استلمت فيه دائمًا مجموعة من العناصر ، إلى وضع خنزير في حالة حصولك على عنصر واحد وفي الحالة التالية ، بدلاً من التحقق من عدد العناصر ، ستحصل على عدد الأحرف أو سمة أخرى ، اعتمادًا على نوع البند. سوف ينقطع منطق البرنامج النصي ، وسوف يتظاهر وقت التشغيل بأن كل شيء على ما يرام.


يساعد إعداد الوضع الصارم على تجنب بعض هذا النوع من المشاكل ، لكنه يتطلب أيضًا المزيد من التعليمات البرمجية منك ، مثل تهيئة المتغيرات والإرسال بشكل صريح.


يتم تمكين هذا الوضع من خلال Set-StrictMode -Version Latest cmdlet ، على الرغم من وجود خيارات أخرى لـ "الدقة" ، فإن خياري هو استخدام الأخير.


في المثال أدناه ، يمسك الوضع الصارم بملكية غير موجودة. نظرًا لوجود عنصر واحد فقط داخل المجلد ، سيكون FileInfo نوع المتغير $Input كنتيجة للتنفيذ ، وليس الصفيف المتوقع للعناصر المقابلة:


صارمة


لتجنب مثل هذه المشكلة ، يجب أن ترسل نتائج cmdlet بشكل صريح إلى صفيف:


 $Items = @( Get-ChildItem C:\Users\snd3r\Nextcloud ) 

اجعله دائمًا تعيين وضع صارم ، سيتيح لك ذلك تجنب نتائج غير متوقعة من تشغيل البرامج النصية الخاصة بك.


خطأ في التعامل


ErrorActionPreference


عند النظر إلى البرامج النصية الخاصة بالأشخاص الآخرين ، على سبيل المثال ، على github ، أرى غالبًا إما أن أتجاهل تمامًا آلية معالجة الأخطاء ، أو تنشيط وضع المتابعة الصامت بشكل صريح في حالة حدوث خطأ. بالتأكيد ، لا تعد مشكلة معالجة الأخطاء أسهل البرامج بشكل عام وفي البرامج النصية بشكل خاص ، لكنها بالتأكيد لا تستحق أن يتم تجاهلها. بشكل افتراضي ، يعرضه PowerShell في حالة حدوث خطأ ويستمر في العمل (قمت بتبسيط المفهوم قليلاً ، ولكن فيما يلي رابط لكتاب git حول هذا الموضوع). يعد هذا مناسبًا إذا كنت في حاجة ماسة إلى توزيع تحديث برنامج يستخدم على نطاق واسع في المجال على جميع الأجهزة ، دون الانتظار حتى ينتشر إلى جميع نقاط نشر sccm أو ينتشر بطريقة أخرى تستخدمها أنت. من غير الجيد مقاطعة العملية وإعادة تشغيلها في حالة إيقاف تشغيل أحد الآلات ، وهذا صحيح.


من ناحية أخرى ، إذا قمت بعمل نسخة احتياطية معقدة من نظام يتكون من أكثر من ملف بيانات واحد لأكثر من جزء واحد من نظام المعلومات ، فأنت بحاجة إلى التأكد من أن النسخة الاحتياطية الخاصة بك متسقة وأنه تم نسخ جميع مجموعات البيانات الضرورية دون أخطاء.


لتغيير سلوك أوامر cmdlets في حالة حدوث خطأ ، هناك متغير عالمي $ErrorActionPreference ، مع قائمة القيم المحتملة التالية: إيقاف ، استعلام ، متابعة ، تعليق ، SilentlyContinue .


نوصيك دائمًا باستخدام قيمة Stop عندما يتوقف تخزين عدد من البرامج النصية أو تعقيدها على المكدس في رأسك ، فمن الأفضل أن تتأكد من أن البرنامج النصي في أي حالة غير متوقعة سوف يوقف عمله ، ولا يكسر الحطب "بهدوء" ، ويكمل التنفيذ "بنجاح".


بالإضافة إلى إيقاف البرنامج النصي في حالة حدوث خطأ ما ، هناك شرط أساسي آخر لاستخدامه - التعامل مع المواقف الاستثنائية. يوجد إنشاء try/catch لهذا الغرض ، لكنه يعمل فقط إذا تسبب الخطأ في إيقاف التنفيذ. لا يجب بالضرورة إيقاف الإيقاف على مستوى البرنامج النصي بأكمله ، يمكن ErrorAction تعيين ErrorAction على مستوى أمر cmdlet بواسطة المعلمة:


 Get-ChildItem 'C:\System Volume Information\' -ErrorAction 'Stop' 

في الواقع ، يعرّف هذا الاحتمال استراتيجيتين منطقيتين: حل جميع الأخطاء "افتراضيًا" وتعيين ErrorAction فقط للأماكن الحرجة التي يمكن من خلالها التعامل معها ؛ إما تمكين على مستوى البرنامج النصي بأكمله عن طريق تحديد قيمة المتغير الشامل وإعداد -ErrorAction 'Continue' في العمليات غير الحرجة. أختار دائمًا الخيار الثاني ، لست مستعجلًا لفرضه عليك ، أوصي مرة واحدة فقط بفهم هذه المشكلة واستخدام هذه الأداة المفيدة.


محاولة / الصيد


في معالج الأخطاء ، يمكنك مطابقة نوع الاستثناء والتعامل مع مؤشر ترابط التنفيذ أو ، على سبيل المثال ، إضافة المزيد من المعلومات. على الرغم من حقيقة أنه باستخدام مشغلي try/catch/throw/trap يمكنك إنشاء التدفق الكامل لتنفيذ البرنامج النصي ، إلا أنه يجب عليك تجنب ذلك بشكل قاطع ، نظرًا لأن مثل هذه الطريقة للتشغيل مع التنفيذ لا تعتبر فقط مضادًا متطرفًا من فئة "goto-noodle" ، لذلك كما يستنزف الأداء بشكل كبير.


 #requires -version 3 $ErrorActionPreference = 'Stop' #   ,    , #          $Logger = Get-Logger "$PSScriptRoot\Log.txt" #    trap { $Logger.AddErrorRecord( $_ ) exit 1 } #    $count = 1; while ( $true ) { try { #   $StorageServers = @( Get-ADGroupMember -Identity StorageServers | Select-Object -Expand Name ) } catch [System.Management.Automation.CommandNotFoundException] { #      ,         throw " Get-ADGroupMember ,    Active Directory module for PowerShell; $( $_.Exception.Message )" } catch [System.TimeoutException] { #             if ( $count -le 3 ) { $count++; Start-Sleep -S 10; continue } #             throw "     -   ,   $count ; $( $_.Exception.Message )" } #         break } 

تجدر الإشارة إلى أن مشغل trap هو فخ خطأ عالمي. يمسك كل شيء لم تتم معالجته بمستويات أقل ، أو يتم طرده من معالج الاستثناء بسبب استحالة تصحيح الموقف بشكل مستقل.


بالإضافة إلى الأسلوب الموجه نحو الاستثناءات الموضح أعلاه ، يوفر PowerShell أيضًا مفاهيم مألوفة أكثر توافقًا مع الأصداف "الكلاسيكية" الأخرى ، مثل تدفقات الأخطاء ، أكواد الإرجاع ، ومتغيرات تراكم الأخطاء. كل هذا مناسب بالتأكيد ، أحيانًا بدون بديل ، لكنه يتجاوز نطاق هذا الموضوع ، بشكل عام ، نظرة عامة. لحسن الحظ ، هناك كتاب مفتوح جيد حول جيثب حول هذا الموضوع.


رمز المسجل الذي أستخدمه عندما لا يكون هناك يقين من أن النظام سيحصل على PowerShell 5 (حيث يمكنك وصف فئة المسجل بطريقة أكثر ملاءمة) ، جربه ، قد يكون مفيدًا لك بسبب بساطته وإيجازه ، ومن المؤكد أنك ستضيف طرقًا إضافية دون صعوبة. :


 #   " ",   PowerShell v3 function Get-Logger { [CmdletBinding()] param ( [Parameter( Mandatory = $true )] [string] $LogPath, [string] $TimeFormat = 'yyyy-MM-dd HH:mm:ss' ) $LogsDir = [System.IO.Path]::GetDirectoryName( $LogPath ) New-Item $LogsDir -ItemType Directory -Force | Out-Null New-Item $LogPath -ItemType File -Force | Out-Null $Logger = [PSCustomObject]@{ LogPath = $LogPath TimeFormat = $TimeFormat } Add-Member -InputObject $Logger -MemberType ScriptMethod AddErrorRecord -Value { param( [Parameter( Mandatory = $true )] [string]$String ) "$( Get-Date -Format 'yyyy-MM-dd HH:mm:ss' ) [Error] $String" | Out-File $this.LogPath -Append } return $Logger } 

أكرر الفكرة - لا تتجاهل معالجة الأخطاء. هذا سيوفر لك الوقت والأعصاب على المدى الطويل.
لا أعتقد أن تشغيل البرنامج النصي بغض النظر عن ما هو جيد. جيد - لقد حان الوقت للسقوط دون كسر الحطب.


الأدوات


بالتأكيد يجب تشغيل أدوات PowerShell باستخدام محاكي وحدة التحكم. كثيرا ما سمعت من مؤيدي الدبابير البديلة أن وحدة التحكم في النوافذ سيئة وأن هذه ليست وحدة تحكم على الإطلاق ، ولكن دوس وهلم جرا. قليلون هم الذين يستطيعون صياغة ادعاءاتهم بشأن هذا الموضوع بشكل كافٍ ، ولكن إذا نجح شخص ما ، فقد تبيّن في الواقع أنه يمكن حل جميع المشكلات. كان هناك بالفعل مزيد من المعلومات حول الأجهزة الطرفية ووحدة التحكم الجديدة في windows على المحور ؛ كل شيء أكثر من موافق هناك .


تتمثل الخطوة الأولى في تثبيت Conemu أو مجموعة Cmder الخاصة به ، وهي ليست مهمة بشكل خاص ، لأنه في رأيي يستحق الأمر تشغيل الإعدادات على أي حال. عادةً ما أختار cmder في الحد الأدنى من التكوين ، بدون gita وغيرها من الثنائيات ، التي حددتها لنفسي ، على الرغم من عدة سنوات قمت بضبط التكوين الخاص بي من أجل Conemu النقي. هذا هو حقًا أفضل محاكي طرفية للنوافذ ، مما يسمح لك بتقسيم الشاشة (لعشاق tmux / الشاشة) ، وإنشاء علامات تبويب وتمكين وضع وحدة التحكم في نمط الزلزال:


كونيمو


اعتدال


الخطوة التالية أوصي بوضع الوحدات النمطية: oh-my-posh و posh-git و PSReadLine . أولهما سيجعل حفلة موسيقية أكثر متعة بإضافة معلومات حول الجلسة الحالية ، وحالة آخر أمر تم تنفيذه ، ومؤشر الامتياز وحالة مستودع git في الموقع الحالي. يعزز PSReadLine promt بشكل كبير ، مضيفًا على سبيل المثال بحثًا عن محفوظات الأوامر التي تم إدخالها (CRTL + R) ونصائح مناسبة لأوامر cmdlets على CRTL + Space:


readline


ونعم ، يمكن الآن مسح وحدة التحكم باستخدام CTRL + L ، ونسيان cls .


رمز الاستوديو البصري


المحرر إن أسوأ ما يمكنني قوله عن PowerShell هو PowerShell ISE البحت ، ومن غير المرجح أن ينسى هؤلاء الذين شاهدوا النسخة الأولى من ثلاث لوحات هذه التجربة. الترميز المختلفة للمحطة ، والافتقار إلى ميزات المحرر الأساسية ، مثل المسافة البادئة التلقائية ، والأقواس ذات الإغلاق التلقائي ، وتنسيق الكود ومجموعة كاملة من مضادات التباين الناتجة عن ذلك والتي لن أخبرك بها (فقط في حالة) - هذا هو كل شيء عن ISE.


لا تستخدمه ، استخدم Visual Studio Code مع ملحق PowerShell - يوجد كل ما لا تريده (ضمن حدود معقولة بالطبع). ولا تنسَ أنه في PoweShell حتى الإصدار السادس (PowerShell Core 6.0) ، يكون ترميز البرامج النصية هو UTF8 BOM ، وإلا فسوف تنقطع اللغة الروسية.


vscode


بالإضافة إلى تسليط الضوء على بناء الجملة ، ونصائح الأدوات وقدرات تصحيح النص ، يثبت البرنامج المساعد linter الذي سيساعدك أيضًا على اتباع الممارسات المعمول بها في المجتمع ، على سبيل المثال ، توسيع الاختصارات بنقرة واحدة (على المصباح الكهربائي). في الواقع ، هذه وحدة نمطية منتظمة يمكن تثبيتها بشكل مستقل ، على سبيل المثال ، قم بإضافتها إلى البرامج النصية لتوقيع خطوط الأنابيب: PSScriptAnalyzer


PSScriptAnalyzer


يمكنك ضبط معلمات تنسيق الكود في إعدادات الامتداد ، ولكل الإعدادات (المحرر والإضافات) ، هناك بحث: File - Preferences - Settings :


أوتبس


من أجل الحصول على وحدة تحكم فارغة جديدة ، يجب عليك تعيين العلامة في الإعدادات ، في وقت لاحق ، من المحتمل أن تكون هذه النصيحة غير ملائمة.


تجدر الإشارة إلى أن أي إجراء في VS Code يمكن تنفيذه من مركز التحكم ، يطلق عليه CTRL + Shift + P. قم بتنسيق جزء من الكود المدرج من الدردشة ، وفرزًا أبجديًا ، وتغيير المسافة البادئة من مسافات إلى علامات تبويب ، وهكذا ، كل هذا في مركز التحكم.


على سبيل المثال ، قم بتمكين وضع محرر الشاشة الكاملة والمركز:


تخطيط


تحكم المصدر جيت ، SVN


غالبًا ما يكون لدى مسؤولي نظام Windows خوف من حل التعارض في أنظمة التحكم في الإصدار ، ربما لأنه إذا كان أحد ممثلي هذه المجموعة يستخدم git ، فغالبًا ما لا يواجه المرء أي مشكلة من هذا النوع. باستخدام vscode ، يتم تقليل دقة التعارض إلى نقرات الماوس حرفيًا على تلك الأجزاء من التعليمات البرمجية التي يجب تركها أو استبدالها.


دمج


هذه التصنيفات بين الأسطر 303 و 304 قابلة للنقر ، ومن الجدير النقر فوق كل تلك التي تظهر في المستند في حالة وجود تعارض ، والالتزام بتنفيذ التغييرات وإرسال التغييرات إلى الخادم. يو - الراحة.


حول كيفية العمل مع أنظمة التحكم في الإصدار المتوفرة ومع الصور التي يتم كتابتها في قفص الاتهام vscode ، تغمض عينيك هناك لفترة وجيزة وبشكل جيد.


قصاصات


المقتطفات هي نوع من الماكرو / القالب الذي يسمح لك بتسريع كتابة التعليمات البرمجية. بالتأكيد يجب أن نرى.


إنشاء كائن سريع:


customobject


الأسماك للحصول على المساعدة القائمة على التعليق:


مساعدة


إذا احتاج الأمر cmdlet إلى اجتياز عدد كبير من المعلمات ، فمن المنطقي استخدام الترشيش .
هنا مقتطف له:


ترشيش


عرض جميع المقتطفات المتوفرة المتوفرة بواسطة Ctrl + Alt + J:


قصاصات


إذا كنت ترغب بعد ذلك في مواصلة تحسين محيطك ، لكنك لم تسمع عن صفائح الزنبور حتى الآن ، فأنت هنا . أيضًا ، إذا كانت لديك مجموعة من الإضافات الخاصة بك مفيدة لك عند كتابة البرامج النصية لـ PowerShell ، فسوف يسعدني أن أرى قائمتهم في التعليقات.


الأداء


موضوع الأداء ليس بسيطًا كما قد يبدو للوهلة الأولى. من ناحية ، يمكن أن تقلل التحسينات المبكرة من قابلية قراءة التعليمات البرمجية وإمكانية صيانتها إلى حد كبير ، مما يوفر 300ms من وقت تنفيذ البرنامج النصي ، ويمكن أن يكون وقت التشغيل المعتاد عشر دقائق ، واستخدامها في هذه الحالة مدمر بالتأكيد. من ناحية أخرى ، هناك العديد من الحيل البسيطة إلى حد ما والتي تزيد من قابلية قراءة الشفرة وسرعتها ، وهي مناسبة تمامًا للاستخدام بشكل مستمر. سوف أتحدث أدناه عن بعضها ، إذا كان الأداء هو كل شيء بالنسبة لك ، وكانت قابلية القراءة على جانب الطريق بسبب القيود الزمنية الصارمة لوقت تعطل الخدمة أثناء الصيانة ، نوصي بالرجوع إلى الأدبيات ذات الصلة .


خط أنابيب و foreach


الطريقة الأسهل والعمل دائمًا لزيادة الإنتاجية هي تجنب استخدام الأنابيب. نظرًا لضمان السلامة والراحة من أجل توفير الطاقة ، يقوم PowerShell بتمرير العناصر عبر أنبوب يلتف كل منها في كائن. في لغات dotnet ، يسمى هذا السلوك الملاكمة . الملاكمة جيدة ، فهي تضمن الأمان ، لكن لها سعرها الخاص ، الذي لا يكون من المنطقي في بعض الأحيان دفعه.


تتمثل الخطوة الأولى في زيادة الأداء ، وفي رأيي ، تحسين قابلية القراءة عن طريق إزالة جميع تطبيقات الأمر Foreach-Object واستبداله ببيان foreach. قد تشعر بالحرج من حقيقة أن هذه هي في الواقع كيانان مختلفان ، لأن foreach هو اسم مستعار لـ Foreach-Object - في الممارسة العملية ، يتمثل الاختلاف الرئيسي في أن foreach لا يقبل القيم من خط أنابيب ، لكنه يعمل عن طريق التجربة بمعدل أسرع ثلاث مرات.


: - , , :


 Get-Content D:\temp\SomeHeavy.log | Select-String '328117' 

— , . — , , Get-Content . string , , . — , :


When reading from and writing to binary files, use the AsByteStream parameter and a value of 0 for the ReadCount parameter. A ReadCount value of 0 reads the entire file in a single read operation. The default ReadCount value, 1, reads one byte in each read operation and converts each byte into a separate object, which causes errors when you use the Set-Content cmdlet to write the bytes to a file unless you use AsByteStream

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-content

 Get-Content D:\temp\SomeHeavy.log -ReadCount 0 | Select-String '328117' 

:


readcount


, . Select-String — . , Select-String . , Select-String , , :


 foreach ( $line in ( Get-Content D:\temp\SomeHeavy.log -ReadCount 0 )) { if ( $line -match '328117' ) { $line } } 

30 , - 30%, , , , - , ( ;-). , . , -match ; — . , — — - , .


— - , " " :


 foreach ( $line in ( Get-Content D:\temp\SomeHeavy.log -ReadCount 0 )) { if ( $line -match '328117' ) { "$( Get-Date -UFormat '%d.%m.%Y %H:%M:%S') $line" | Out-File D:\temp\Result.log -Append } } 

Measure-Command :


 Hours : 2 Minutes : 20 Seconds : 9 Milliseconds : 101 

. , , , . , PowerShell , , — . , , — StringBuilder . , , . , .


 $StringBuilder = New-Object System.Text.StringBuilder foreach ( $line in ( Get-Content D:\temp\SomeHeavy.log -ReadCount 0 )) { if ( $line -match '328117' ) { $null = $StringBuilder.AppendLine( "$( Get-Date -UFormat '%d.%m.%Y %H:%M:%S') $line" ) } } Out-File -InputObject $StringBuilder.ToString() -FilePath D:\temp\Result.log -Append -Encoding UTF8 

5 , :


 Hours : 0 Minutes : 5 Seconds : 37 Milliseconds : 150 

Out-File -InputObject , , . — . — Get-Help -Full , Accept pipeline input? true (ByValue) :


 -InputObject <psobject> Required? false Position? Named Accept pipeline input? true (ByValue) Parameter set name (All) Aliases None Dynamic? false 

PowerShell :


taskmgr


StringBuilder :


ستاربويلدر تخصيص


, , 3 3. dotnet- — StreamReader .


 $StringBuilder = New-Object System.Text.StringBuilder $StreamReader = New-Object System.IO.StreamReader 'D:\temp\SomeHeavy.log' while ( $line = $StreamReader.ReadLine()) { if ( $line -match '328117' ) { $null = $StringBuilder.AppendLine( "$( Get-Date -UFormat '%d.%m.%Y %H:%M:%S') $line" ) } } $StreamReader.Dispose() Out-File -InputObject $StringBuilder.ToString() -FilePath C:\temp\Result.log -Append -Encoding UTF8 

 Hours : 0 Minutes : 5 Seconds : 33 Milliseconds : 657 

, . , , , , 2. , :


تيار


— "", — StringBuilder — "" . , ( 100) . — 90% ( , ):


 $BufferSize = 104857600 $StringBuilder = New-Object System.Text.StringBuilder $BufferSize $StreamReader = New-Object System.IO.StreamReader 'C:\temp\SomeHeavy.log' while ( $line = $StreamReader.ReadLine()) { if ( $line -match '1443' ) { #      if ( $StringBuilder.Length -gt ( $BufferSize - ( $BufferSize * 0.1 ))) { Out-File -InputObject $StringBuilder.ToString() -FilePath C:\temp\Result.log -Append -Encoding UTF8 $StringBuilder.Clear() } $null = $StringBuilder.AppendLine( "$( Get-Date -UFormat '%d.%m.%Y %H:%M:%S') $line" ) } } Out-File -InputObject $StringBuilder.ToString() -FilePath C:\temp\Result.log -Append -Encoding UTF8 $StreamReader.Dispose() 

 Hours : 0 Minutes : 5 Seconds : 53 Milliseconds : 417 

1 :


streamreader مع تفريغ


, . , , StreamWriter , , ;-) , , .


- — , . , — . Select-String Out-File , OutOfMemoryException , — .



, PowerShell , — , : PowerShell — , .


, StringBuilder dir — ( ). :


 $CurrentPath = ( Get-Location ).Path + '\' $StringBuilder = New-Object System.Text.StringBuilder foreach ( $Line in ( &cmd /c dir /b /s /ad )) { $null = $StringBuilder.AppendLine( $Line.Replace( $CurrentPath, '.' )) } $StringBuilder.ToString() 

 Hours : 0 Minutes : 0 Seconds : 3 Milliseconds : 9 

 $StringBuilder = New-Object System.Text.StringBuilder foreach ( $Line in ( Get-ChildItem -File -Recurse | Resolve-Path -Relative )) { $null = $StringBuilder.AppendLine( $Line ) } $StringBuilder.ToString() 

 Hours : 0 Minutes : 0 Seconds : 16 Milliseconds : 337 

$null — . , — Out-Null ; , ( $null ) , .


 # : $null = $StringBuilder.AppendLine( $Line ) # : $StringBuilder.AppendLine( $Line ) | Out-Null 

, , . Compare-Object , , , . robocopy.exe, ( PowerShell 5), :


 class Robocopy { [String]$RobocopyPath Robocopy () { $this.RobocopyPath = Join-Path $env:SystemRoot 'System32\Robocopy.exe' if ( -not ( Test-Path $this.RobocopyPath -PathType Leaf )) { throw '    ' } } [void]CopyFile ( [String]$SourceFile, [String]$DestinationFolder ) { $this.CopyFile( $SourceFile, $DestinationFolder, $false ) } [void]CopyFile ( [String]$SourceFile, [String]$DestinationFolder, [bool]$Archive ) { $FileName = [IO.Path]::GetFileName( $SourceFile ) $FolderName = [IO.Path]::GetDirectoryName( $SourceFile ) $Arguments = @( '/R:0', '/NP', '/NC', '/NS', '/NJH', '/NJS', '/NDL' ) if ( $Archive ) { $Arguments += $( '/A+:a' ) } $ErrorFlag = $false &$this.RobocopyPath $FolderName $DestinationFolder $FileName $Arguments | Foreach-Object { if ( $ErrorFlag ) { $ErrorFlag = $false throw "$_ $ErrorString" } else { if ( $_ -match '(?<=\(0x[\da-f]{8}\))(?<text>(.+$))' ) { $ErrorFlag = $true $ErrorString = $matches.text } else { $Logger.AddRecord( $_.Trim()) } } } if ( $LASTEXITCODE -eq 8 ) { throw 'Some files or directories could not be copied' } if ( $LASTEXITCODE -eq 16 ) { throw 'Robocopy did not copy any files. Check the command line parameters and verify that Robocopy has enough rights to write to the destination folder.' } } [void]SyncFolders ( [String]$SourceFolder, [String]$DestinationFolder ) { $this.SyncFolders( $SourceFolder, $DestinationFolder, '*.*', '', $false ) } [void]SyncFolders ( [String]$SourceFolder, [String]$DestinationFolder, [Bool]$Archive ) { $this.SyncFolders( $SourceFolder, $DestinationFolder, '*.*', '', $Archive ) } [void]SyncFolders ( [String]$SourceFolder, [String]$DestinationFolder, [String]$Include ) { $this.SyncFolders( $SourceFolder, $DestinationFolder, $Include, '', $false ) } [void]SyncFolders ( [String]$SourceFolder, [String]$DestinationFolder, [String]$Include, [Bool]$Archive ) { $this.SyncFolders( $SourceFolder, $DestinationFolder, $Include, '', $Archive ) } [void]SyncFolders ( [String]$SourceFolder, [String]$DestinationFolder, [String]$Include, [String]$Exclude ) { $this.SyncFolders( $SourceFolder, $DestinationFolder, $Include, $Exclude, $false ) } [void]SyncFolders ( [String]$SourceFolder, [String]$DestinationFolder, [String]$Include, [String]$Exclude, [Bool]$Archive ) { $Arguments = @( '/MIR', '/R:0', '/NP', '/NC', '/NS', '/NJH', '/NJS', '/NDL' ) if ( $Exclude ) { $Arguments += $( '/XF' ) $Arguments += $Exclude.Split(' ') } if ( $Archive ) { $Arguments += $( '/A+:a' ) } $ErrorFlag = $false &$this.RobocopyPath $SourceFolder $DestinationFolder $Include $Arguments | Foreach-Object { if ( $ErrorFlag ) { $ErrorFlag = $false throw "$_ $ErrorString" } else { if ( $_ -match '(?<=\(0x[\da-f]{8}\))(?<text>(.+$))' ) { $ErrorFlag = $true $ErrorString = $matches.text } else { $Logger.AddRecord( $_.Trim()) } } } if ( $LASTEXITCODE -eq 8 ) { throw 'Some files or directories could not be copied' } if ( $LASTEXITCODE -eq 16 ) { throw 'Robocopy did not copy any files. Check the command line parameters and verify that Robocopy has enough rights to write to the destination folder.' } } } 

, ( ), — .


, : Foreach-Object !? , : foreach , Foreach-Object — , , , , . .


, :


 $Robocopy = New-Object Robocopy #    $Robocopy.CopyFile( $Source, $Dest ) #   $Robocopy.SyncFolders( $SourceDir, $DestDir ) #    .xml     $Robocopy.SyncFolders( $SourceDir, $DestDir , '*.xml', $true ) #     *.zip *.tmp *.log     $Robocopy.SyncFolders( $SourceDir, $DestDir, '*.*', '*.zip *.tmp *.log', $true ) 


— , , ; , , , :


  • foreach Foreach-Object ;


  • ;


  • / , ;


  • StringBuilder ;


  • , - ;


  • ( "" );



: - , .


وظائف


, , , , , , . . , IO, .


ssd

Windows Server 2019 Hyper-V ssd ( hdd):


2019ssd


PowerShell ( Get-Command *-Job ), .


, , , :


 $Job = Start-Job -ScriptBlock { Write-Output 'Good night' Start-Sleep -S 10 Write-Output 'Good morning' } $Job | Wait-Job | Receive-Job Remove-Job $Job 

, — , . .


, :


وظائف
https://xaegr.wordpress.com/2011/07/12/threadping/


, , — , . , , (50 — 50 ):


وظيفة يموت


. , — , . — , .


, , , - .


Runspaces


Beginning Use of PowerShell Runspaces: Part 1 . , — PowerShell , . (, PowerShell ), : ( ) . , .


WPF , PowerShell, . — , . — , "" . .


, .


wpf


 #     $GUISyncHash = [hashtable]::Synchronized(@{}) <# WPF  #> $GUISyncHash.FormXAML = [xml](@" <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Sample WPF Form" Height="510" Width="410" ResizeMode="NoResize"> <Grid> <Label Content=" " HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="37" Width="374" FontSize="18"/> <Label Content="" HorizontalAlignment="Left" Margin="16,64,0,0" VerticalAlignment="Top" Height="26" Width="48"/> <TextBox x:Name="BackupPath" HorizontalAlignment="Left" Height="23" Margin="69,68,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="300"/> <Label Content="" HorizontalAlignment="Left" Margin="16,103,0,0" VerticalAlignment="Top" Height="26" Width="35"/> <TextBox x:Name="RestorePath" HorizontalAlignment="Left" Height="23" Margin="69,107,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="300"/> <Button x:Name="FirstButton" Content="√" HorizontalAlignment="Left" Margin="357,68,0,0" VerticalAlignment="Top" Width="23" Height="23"/> <Button x:Name="SecondButton" Content="√" HorizontalAlignment="Left" Margin="357,107,0,0" VerticalAlignment="Top" Width="23" Height="23"/> <CheckBox x:Name="Check" Content="  " HorizontalAlignment="Left" Margin="16,146,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.113,-0.267" Width="172"/> <Button x:Name="Go" Content="Go" HorizontalAlignment="Left" Margin="298,173,0,0" VerticalAlignment="Top" Width="82" Height="26"/> <ComboBox x:Name="Droplist" HorizontalAlignment="Left" Margin="16,173,0,0" VerticalAlignment="Top" Width="172" Height="26"/> <ListBox x:Name="ListBox" HorizontalAlignment="Left" Height="250" Margin="16,210,0,0" VerticalAlignment="Top" Width="364"/> </Grid> </Window> "@) <#   #> $GUISyncHash.GUIThread = { $GUISyncHash.Window = [Windows.Markup.XamlReader]::Load(( New-Object System.Xml.XmlNodeReader $GUISyncHash.FormXAML )) $GUISyncHash.Check = $GUISyncHash.Window.FindName( "Check" ) $GUISyncHash.GO = $GUISyncHash.Window.FindName( "Go" ) $GUISyncHash.ListBox = $GUISyncHash.Window.FindName( "ListBox" ) $GUISyncHash.BackupPath = $GUISyncHash.Window.FindName( "BackupPath" ) $GUISyncHash.RestorePath = $GUISyncHash.Window.FindName( "RestorePath" ) $GUISyncHash.FirstButton = $GUISyncHash.Window.FindName( "FirstButton" ) $GUISyncHash.SecondButton = $GUISyncHash.Window.FindName( "SecondButton" ) $GUISyncHash.Droplist = $GUISyncHash.Window.FindName( "Droplist" ) $GUISyncHash.Window.Add_SourceInitialized({ $GUISyncHash.GO.IsEnabled = $true }) $GUISyncHash.FirstButton.Add_Click( { $GUISyncHash.ListBox.Items.Add( 'Click FirstButton' ) }) $GUISyncHash.SecondButton.Add_Click( { $GUISyncHash.ListBox.Items.Add( 'Click SecondButton' ) }) $GUISyncHash.GO.Add_Click( { $GUISyncHash.ListBox.Items.Add( 'Click GO' ) }) $GUISyncHash.Window.Add_Closed( { Stop-Process -Id $PID -Force }) $null = $GUISyncHash.Window.ShowDialog() } $Runspace = @{} $Runspace.Runspace = [RunspaceFactory]::CreateRunspace() $Runspace.Runspace.ApartmentState = "STA" $Runspace.Runspace.ThreadOptions = "ReuseThread" $Runspace.Runspace.Open() $Runspace.psCmd = { Add-Type -AssemblyName PresentationCore, PresentationFramework, WindowsBase }.GetPowerShell() $Runspace.Runspace.SessionStateProxy.SetVariable( 'GUISyncHash', $GUISyncHash ) $Runspace.psCmd.Runspace = $Runspace.Runspace $Runspace.Handle = $Runspace.psCmd.AddScript( $GUISyncHash.GUIThread ).BeginInvoke() Start-Sleep -S 1 $GUISyncHash.ListBox.Dispatcher.Invoke( "Normal", [action] { $GUISyncHash.ListBox.Items.Add( '' ) }) $GUISyncHash.ListBox.Dispatcher.Invoke( "Normal", [action] { $GUISyncHash.ListBox.Items.Add( '  ' ) }) foreach ( $item in 1..5 ) { $GUISyncHash.Droplist.Dispatcher.Invoke( "Normal", [action] { $GUISyncHash.Droplist.Items.Add( $item ) $GUISyncHash.Droplist.SelectedIndex = 0 }) } $GUISyncHash.ListBox.Dispatcher.Invoke( "Normal", [action] { $GUISyncHash.ListBox.Items.Add( 'While ( $true ) { Start-Sleep -S 10 }' ) }) while ( $true ) { Start-Sleep -S 10 } 

WPF github, , smart : https://github.com/snd3r/GetDiskSmart/ . , MVVM:


ينبض


Visual Studio, Community Edition , xaml- wpf — https://github.com/punker76/kaxaml


kaxaml


بدلا من الاستنتاج


PowerShell — Windows-. , , , .


, , "PowerShell, ", . , — , . . , - , - .


— .


الهدوء


PS Boomburum , 2019 powershell — .

Source: https://habr.com/ru/post/ar443256/


All Articles