From 01cd03b35568098a6ed1959b9a1e0bdd693f4ba9 Mon Sep 17 00:00:00 2001 From: li-so Date: Sat, 25 Jul 2015 18:56:04 -0600 Subject: [PATCH 001/113] Update Function Structure.md Moved brace to same line as per style guide. --- Style Guide/Function Structure.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Style Guide/Function Structure.md b/Style Guide/Function Structure.md index 245a81a..c023865 100644 --- a/Style Guide/Function Structure.md +++ b/Style Guide/Function Structure.md @@ -5,9 +5,8 @@ Avoid using the `return` keyword in your functions. Just place the object variab When declaring simple functions leave a space between the function name and the parameters. ```PowerShell -function MyFunction ($param1, $param2) -{ - +function MyFunction ($param1, $param2) { + ... } ``` From cb665de596ec62ba6ad72b99dbf3e21ccf2d0ba3 Mon Sep 17 00:00:00 2001 From: Sean Wheeler Date: Fri, 17 Jun 2016 08:45:52 -0700 Subject: [PATCH 002/113] Fix broken link The link to Documentation and Comments was broken. --- Style Guide/Introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style Guide/Introduction.md b/Style Guide/Introduction.md index 0872c53..f2ee3bd 100644 --- a/Style Guide/Introduction.md +++ b/Style Guide/Introduction.md @@ -10,7 +10,7 @@ This document is an attempt to come to an agreement on a style-guide because we - [Code Layout and Formatting](Code Layout and Formatting.md) - [Function Structure](Function Structure.md) -- [Documentation and Commenting](Documentation and Commenting.md) +- [Documentation and Comments](Style Guide/Documentation and Comments.md) - [Readability](Readability.md) - [Naming Conventions](Naming Conventions.md) From dedee305c56dc9434ac742c8affddd198c4b5f5b Mon Sep 17 00:00:00 2001 From: "Nicholas M. Getchell" Date: Mon, 25 Jul 2016 08:48:43 -0400 Subject: [PATCH 003/113] Fixes 404 A bad link on "Style Guide\Introduction.md" that results in a 404. Removed the redundant "Style Guide" folder and the link works again. --- Style Guide/Introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style Guide/Introduction.md b/Style Guide/Introduction.md index f2ee3bd..c79fac7 100644 --- a/Style Guide/Introduction.md +++ b/Style Guide/Introduction.md @@ -10,7 +10,7 @@ This document is an attempt to come to an agreement on a style-guide because we - [Code Layout and Formatting](Code Layout and Formatting.md) - [Function Structure](Function Structure.md) -- [Documentation and Comments](Style Guide/Documentation and Comments.md) +- [Documentation and Comments](Documentation and Comments.md) - [Readability](Readability.md) - [Naming Conventions](Naming Conventions.md) From 6226d05e36b87198f651c3a788aa037736936db2 Mon Sep 17 00:00:00 2001 From: Ryan Spletzer Date: Mon, 17 Oct 2016 08:35:54 -0400 Subject: [PATCH 004/113] Inserted space after param keyword --- Style Guide/Function Structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style Guide/Function Structure.md b/Style Guide/Function Structure.md index 511d80d..25d2fd3 100644 --- a/Style Guide/Function Structure.md +++ b/Style Guide/Function Structure.md @@ -56,7 +56,7 @@ function Get-USCitizenCapability { function Get-USCitizenCapability { [CmdletBinding()] [OutputType([psobject])] - param( + param ( [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0)] From 00ce657569d3a3f2aab741f3eeca1547a000f515 Mon Sep 17 00:00:00 2001 From: Thomas Rhoads Date: Wed, 7 Dec 2016 09:35:24 -0500 Subject: [PATCH 005/113] Fixed typo in Best Practices/Error Handling.md --- Best Practices/Error Handling.md | 140 +++++++++++++++---------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/Best Practices/Error Handling.md b/Best Practices/Error Handling.md index e4ab42e..b9de486 100644 --- a/Best Practices/Error Handling.md +++ b/Best Practices/Error Handling.md @@ -1,70 +1,70 @@ -# ERR-01 Use -ErrorAction Stop when calling cmdlets - -When trapping an error, try to use -ErrorAction Stop on cmdlets to generate terminating, trappable exceptions. - -# ERR-02 Use $ErrorActionPreference='Stop' or 'Continue' when calling non-cmdlets - -When executing something other than a cmdlet, set $ErrorActionPreference='Stop' before executing, and re-set to Continue afterwards. If you're concerned about using -ErrorAction because it will bail on the entire pipeline, then you've probably over-constructed the pipeline. Consider using a more scripting-construct-style approach, because those approaches are inherently better for automated error handling. - -Ideally, whatever command or code you think might bomb should be dealing with one thing: querying one computer, deleting one file, updating one user. That way, if an error occurs, you can handle it and then get on with the next thing. - -# ERR-03 Avoid using flags to handle errors - -Try to avoid setting flags: - -```PowerShell -try { - $continue = $true - Do-Something -ErrorAction Stop -} catch { - $continue = $false -} - -if ($continue) { - Do-This - Set-That - Get-Those -} -``` - -Instead, put the entire "transaction" into the Try block: - -```PowerShell -try { - Do-Something -ErrorAction Stop - Do-This - Set-That - Get-Those -} catch { - Handle-Error -} -``` - -It's a lot easier to follow the logic. - -# ERR-04 Avoid using $? - -When you need to examine the error that occurred, try to avoid using $?. It actually doesn't mean an error did or did not occur; it's reporting whether or not the last-run command considered itself to have completed successfully. You get no details on what happened. - - -# ERR-05 Avoid testing for a null variable as an error condition - -Also try to avoid testing for a null variable as an error condition: - -``` -$user = Get-ADUser -Identity DonJ - -if ($user) { - $user | Do-Something -} else [ - Write-Warning "Could not get user $user" -} -``` - -There are times and technologies where that's the only approach that will work, especially if the command you're running won't produce a terminating, trappable exception. But it's a logically contorted approach, and it can make debugging trickier. - -# ERR-06 Copy $Error[0] to your own variable - -Within a `catch` block, `$_` will contain the last error that occurred, as will `$Error[0]`. Use either - but immediately copy them into your own variable, as executing additional commands can cause `$_` to get "hijacked" or `$Error[0]` to contain a different error. - -It isn't necessary to clear `$Error` in most cases. `$Error[0]` will be the last error, and PowerShell will maintain the rest of the `$Error` collection automatically. +# ERR-01 Use -ErrorAction Stop when calling cmdlets + +When trapping an error, try to use -ErrorAction Stop on cmdlets to generate terminating, trappable exceptions. + +# ERR-02 Use $ErrorActionPreference='Stop' or 'Continue' when calling non-cmdlets + +When executing something other than a cmdlet, set $ErrorActionPreference='Stop' before executing, and re-set to Continue afterwards. If you're concerned about using -ErrorAction because it will bail on the entire pipeline, then you've probably over-constructed the pipeline. Consider using a more scripting-construct-style approach, because those approaches are inherently better for automated error handling. + +Ideally, whatever command or code you think might bomb should be dealing with one thing: querying one computer, deleting one file, updating one user. That way, if an error occurs, you can handle it and then get on with the next thing. + +# ERR-03 Avoid using flags to handle errors + +Try to avoid setting flags: + +```PowerShell +try { + $continue = $true + Do-Something -ErrorAction Stop +} catch { + $continue = $false +} + +if ($continue) { + Do-This + Set-That + Get-Those +} +``` + +Instead, put the entire "transaction" into the Try block: + +```PowerShell +try { + Do-Something -ErrorAction Stop + Do-This + Set-That + Get-Those +} catch { + Handle-Error +} +``` + +It's a lot easier to follow the logic. + +# ERR-04 Avoid using $? + +When you need to examine the error that occurred, try to avoid using $?. It actually doesn't mean an error did or did not occur; it's reporting whether or not the last-run command considered itself to have completed successfully. You get no details on what happened. + + +# ERR-05 Avoid testing for a null variable as an error condition + +Also try to avoid testing for a null variable as an error condition: + +``` +$user = Get-ADUser -Identity DonJ + +if ($user) { + $user | Do-Something +} else { + Write-Warning "Could not get user $user" +} +``` + +There are times and technologies where that's the only approach that will work, especially if the command you're running won't produce a terminating, trappable exception. But it's a logically contorted approach, and it can make debugging trickier. + +# ERR-06 Copy $Error[0] to your own variable + +Within a `catch` block, `$_` will contain the last error that occurred, as will `$Error[0]`. Use either - but immediately copy them into your own variable, as executing additional commands can cause `$_` to get "hijacked" or `$Error[0]` to contain a different error. + +It isn't necessary to clear `$Error` in most cases. `$Error[0]` will be the last error, and PowerShell will maintain the rest of the `$Error` collection automatically. From b29161cbb6da9198e9a14492ce18fea8f0bb3e39 Mon Sep 17 00:00:00 2001 From: Sergei Ryabkov Date: Sat, 24 Dec 2016 08:18:24 -0500 Subject: [PATCH 006/113] Fix a little spelling error "use" was missing an "e" at the end --- Style Guide/Code Layout and Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style Guide/Code Layout and Formatting.md b/Style Guide/Code Layout and Formatting.md index cc24a43..c57bfe2 100644 --- a/Style Guide/Code Layout and Formatting.md +++ b/Style Guide/Code Layout and Formatting.md @@ -101,7 +101,7 @@ Lines should not have trailing whitespace. Extra spaces result in future edits w #### Spaces around parameters and operators -You should us a single space around parameter names and operators, including comparison operators and math and assignment operators, even when the spaces are not necessary for PowerShell to correctly parse the code. +You should use a single space around parameter names and operators, including comparison operators and math and assignment operators, even when the spaces are not necessary for PowerShell to correctly parse the code. One notable exception is when using semi-colons to pass values to switch parameters: From e7bc92d4f8627ffb7a825d02940f4f0b7fe55a8c Mon Sep 17 00:00:00 2001 From: Sergei Ryabkov Date: Sat, 24 Dec 2016 08:32:16 -0500 Subject: [PATCH 007/113] Add links to to-do items Add links to to-do items to make reading work-in-progress content easier --- Best Practices/Naming Conventions.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Best Practices/Naming Conventions.md b/Best Practices/Naming Conventions.md index 6f23cea..cabba4a 100644 --- a/Best Practices/Naming Conventions.md +++ b/Best Practices/Naming Conventions.md @@ -1,2 +1,3 @@ -TODO: Copy #36 Capitalization guidelines -TODO: Copy #23 Command Prefixes \ No newline at end of file +TODO: Copy [#36](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/36) Capitalization guidelines + +TODO: Copy [#23](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/23) Command Prefixes From b1bf9cf1e0b2501d8fc729dfdceb861d9af4cccc Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Fri, 10 Mar 2017 17:41:19 -0500 Subject: [PATCH 008/113] Fix #36: add capitalization guidelines --- Style Guide/Code Layout and Formatting.md | 90 ++++++++++++++++++----- 1 file changed, 73 insertions(+), 17 deletions(-) diff --git a/Style Guide/Code Layout and Formatting.md b/Style Guide/Code Layout and Formatting.md index c57bfe2..d7092fd 100644 --- a/Style Guide/Code Layout and Formatting.md +++ b/Style Guide/Code Layout and Formatting.md @@ -3,20 +3,78 @@ ### Code Layout & Formatting -Please note that many of these guidelines, in particular, are purely about readability. Some of them are arbitrary rules, but they are based on decades of traditions in programming, so while you may disagree with some rules (and should always follow the rules of individual projects), when we ask you to leave an empty line after a closing function brace, or two lines before functions, we're not being capricious, we're doing so because it makes it easier for experienced developers to scan your code. +These guidelines are about readability. Some of them are arbitrary rules, but they are based on decades of traditions in programming, so while you may disagree with some rules (and should always follow the rules of individual projects), when we ask you to leave an empty line after a closing function brace, or two lines before functions, we're not being capricious, we're doing so because it makes it easier for experienced developers to scan your code. #### Maintain consistency in layout -Rules about indentation and line length are about consistency across code bases. Long practice has shown that it's easier to read and understand code when it looks familiar and you're not being distracted by details, which means that (as with the python community), it's better for everyone to follow a single set of rules. +Rules about indentation, line length, and capitalization are about consistency across code bases. Long practice has shown that it's easier to read and understand code when it looks familiar and you're not being distracted by details, which means that it's better for everyone in the community to follow a single set of rules. -We aren't trying to force the whole world to change overnight, and the code layout rules for individual projects trump these rules. Whether for legacy reasons, or to match guidelines for multiple languages in a single project, different projects may have different style guides, and since the goal is consistency, you should always abide by the rules in place on your project. +We don't expect everyone to follow these guidelines, and rules for individual projects always trump these. Whether for legacy reasons, or to match guidelines for multiple languages in a single project, different projects may have different style guidelines. Since the goal is consistency, you should always abide by any style rules that are in place on the project you are contributing to. -If you do have a legacy project that is in source control and you decide to reformat code to adopt these rules, try to make all of your whitespace changes in a single a commit that does _nothing_ but edit the whitespace. You should never reformat the whitespace on a file as _part_ of a content change. +If you do have a legacy project that is in source control and you decide to reformat code to adopt these rules, try to make all of your whitespace changes in a single a commit that does _nothing_ but edit the whitespace. You should never reformat the whitespace on a file as _part_ of a content change because it makes the changes hard to spot. +#### Capitalization Conventions -#### Always write CmdletBinding +PowerShell is **not** case sensitive, but we follow capitalization conventions to make code easy to read. They are based on the [capitalization conventions](https://msdn.microsoft.com/en-us/library/ms229043) Microsoft created for the .NET framework, since PowerShell is a .NET scripting language, and PowerShell cmdlets are primarily written in .NET languages following those guidelines. -Let's just get this out of the way: all of your scripts should start life as something like this snippet: +##### Terminology + +* lowercase - all lowercase, no word separation +* UPPERCASE - all capitals, no word separation +* PascalCase - capitalize the first letter of each word +* camelCase - capitalize the first letter of each word _except_ the first. + +PowerShell uses PascalCase for _all_ public identifiers: module names, function or cmdlet names, class, enum, and attribute names, public fields or properties, global variables and constants, etc. In fact, since the _parameters_ to PowerShell commands are actually _properties_ of .Net classes, even parameters use PascalCase rather than camelCase. + +PowerShell language keywords are written in lower case (yes, even `foreach` and `dynamicparam`), as well as operators such as `-eq` and `-match`. The keywords in comment-based help are written in UPPERCASE to make it easy to spot them among the dense prose of documentation. + +```posh +function Write-Host { + <# + .SYNOPSIS + Writes customized output to a host. + .DESCRIPTION + The Write-Host cmdlet customizes output. You can specify the color of text by using + the ForegroundColor parameter, and you can specify the background color by using the + BackgroundColor parameter. The Separator parameter lets you specify a string to use to + separate displayed objects. The particular result depends on the program that is + hosting Windows PowerShell. + #> + [CmdletBinding()] + param( + [Parameter(Position=0, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)] + [PSObject] + $Object, + + [Switch] + $NoNewline, + + [PSObject] + $Separator, + + [System.ConsoleColor] + $ForegroundColor, + + [System.ConsoleColor] + $BackgroundColor + ) + begin + { + ... +``` + +PowerShell uses PascalCase for _all_ public identifiers: module names, function or cmdlet names, class and enum names, public fields or properties, global variables and constants, etc. In fact, since the _parameters_ to PowerShell commands are actually _properties_ of .Net classes, even parameters use PascalCase rather than camelCase. Function names should follow PowerShell's `Verb-Noun` naming conventions, using PascalCase within both Verb and Noun. + +A special case is made for two-letter acronyms in which both letters are capitalized, as in the variable `$PSBoundParameters` or the command `Get-PSDrive`. Note that ([as specified in the .NET guidelines](https://msdn.microsoft.com/en-us/library/ms229043#Anchor_1)) this does not affect the commonly capitalized (but not acronym) words "OK" and "ID" . You should also not extend it to compound acronyms, such as when Azure's Resource Manager (RM) meets a Virtual Machine (VM) in `Start-AzureRmVM`... + +> We are aware that there are **many** places where these conventions have not been followed properly for various reasons -- you should consider these _exceptions_ (such as for COM interop) or _mistakes_ (such as `System.Data.SqlClient.SQLDebugging`), but not a reason for you to disregard the conventions. + +If you wish, you may use camelCase for variables within your functions (or modules) to distinguish _private_ variables from parameters, but this is a matter of taste. Shared variables should be distinguished by using their scope name, such as `$Script:PSBoundParameters` or `$Global:DebugPreference`. + + +#### Always Start With CmdletBinding + +All of your scripts or functions should start life as something like this snippet: ``` [CmdletBinding()]param() @@ -24,9 +82,7 @@ process{} end{} ``` -You can always ignore one of the blocks, and add `begin`, add parameters and so on, but you should never write a script without CmdletBinding, and you should never write one without at least _considering_ making it take pipeline input. - - +You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and so on, but you should avoid writing scripts or functions without CmdletBinding, and you should always at least _consider_ making it take pipeline input. #### Open braces on the same line Code folding is nicer in many editors. @@ -48,7 +104,7 @@ That's the order PowerShell will execute it in This is what PowerShell ISE does and understands, and it's the default for most code editors. As always, existing projects may have different standards, but for public code, please stick to 4 spaces, and the rest of us will try to do the same. The 4-space rule is optional for continuation lines. Hanging indents (when indenting a wrapped command which was too long) may be indented more than one indentation level, or may even be indented an odd number of spaces to line up with a method call or parameter block. - + ```PowerShell # This is ok @@ -58,7 +114,7 @@ $MyObj.GetData( $Param3, $Param4 ) - + # This is better $MyObj.GetData($Param1, $Param2, @@ -69,7 +125,7 @@ $MyObj.GetData($Param1, #### Maximum Line Length -Limit lines to 115 characters when possible. +Limit lines to 115 characters when possible. The PowerShell console is, by default, 120 characters wide, but it allows only 119 characters on output lines, and when entering multi-line text, PowerShell uses a line continuation prompt: `>>> ` and thus limits your line length to 116 anyway. @@ -78,7 +134,7 @@ Most of us work on widescreen monitors these days, and there is little reason to The preferred way to avoid long lines is to use splatting (see [About_Splatting](https://technet.microsoft.com/en-us/library/jj672955.aspx)) and PowerShell's implied line continuation inside parentheses, brackets, and braces -- these should always be used in preference to the backtick for line continuation when applicable, even for strings: ``` -Write-Host ("This is an incredibly important, and extremely long message. " + +Write-Host ("This is an incredibly important, and extremely long message. " + "We cannot afford to leave any part of it out, nor do we want line-breaks in the output. " + "Using string concatenation let's us use short lines here, and still get a long line in the output") ``` @@ -117,14 +173,14 @@ $variable = Get-Content -Path $FilePath -Wait:($ReadCount -gt 0) -TotalCount ($R White-space is (mostly) irrelevant to PowerShell, but its proper use is the key to writing easily readable code. -Use a single space after commas and semicolons, and around pairs of curly braces. +Use a single space after commas and semicolons, and around pairs of curly braces. -Avoid extra spaces inside parenthesis or square braces. +Avoid extra spaces inside parenthesis or square braces. Nested expressions `$( ... )` and script blocks `{ ... }` should have a single space _inside_ them to make code stand out and be more readable. Nested expressions `$( ... )` and variable delimiters `${...}` inside strings do not need spaces _outside_, since that would become a part of the string. - + #### Avoid using semicolons (`;`) at the end of each line. @@ -138,5 +194,5 @@ $Options = @{ Margin = 2 Padding = 2 FontSize = 24 -} +} ``` From c22c5eb551ce62f6deb8457546901efb73d571f0 Mon Sep 17 00:00:00 2001 From: Szeraax Date: Fri, 24 Mar 2017 11:34:39 -0600 Subject: [PATCH 009/113] Update Code Layout and Formatting.md Updated the case of two-letter acronyms that start a variable name when using camelCase. --- Style Guide/Code Layout and Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style Guide/Code Layout and Formatting.md b/Style Guide/Code Layout and Formatting.md index d7092fd..a6e6be7 100644 --- a/Style Guide/Code Layout and Formatting.md +++ b/Style Guide/Code Layout and Formatting.md @@ -69,7 +69,7 @@ A special case is made for two-letter acronyms in which both letters are capital > We are aware that there are **many** places where these conventions have not been followed properly for various reasons -- you should consider these _exceptions_ (such as for COM interop) or _mistakes_ (such as `System.Data.SqlClient.SQLDebugging`), but not a reason for you to disregard the conventions. -If you wish, you may use camelCase for variables within your functions (or modules) to distinguish _private_ variables from parameters, but this is a matter of taste. Shared variables should be distinguished by using their scope name, such as `$Script:PSBoundParameters` or `$Global:DebugPreference`. +If you wish, you may use camelCase for variables within your functions (or modules) to distinguish _private_ variables from parameters, but this is a matter of taste. Shared variables should be distinguished by using their scope name, such as `$Script:PSBoundParameters` or `$Global:DebugPreference`. If you are using camelCase for a variable that starts with a two-letter acronym (where both letters are capitalized), both letters should be set to lowercase (such as `adComputer`). #### Always Start With CmdletBinding From da32154922a092977fee552eb147b9d26b73ab9b Mon Sep 17 00:00:00 2001 From: Chris Dent Date: Sat, 8 Apr 2017 15:18:43 +0100 Subject: [PATCH 010/113] Tweaked OutputType / ParameterSetName headings --- Style Guide/Function Structure.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Style Guide/Function Structure.md b/Style Guide/Function Structure.md index cc57691..ba9ee17 100644 --- a/Style Guide/Function Structure.md +++ b/Style Guide/Function Structure.md @@ -83,17 +83,16 @@ function Get-USCitizenCapability { #### Always have at least a `process {}` code block if any parameters takes values from the Pipeline. -#### Specify an OutputType attribute if the advanced function returns - an object or collection of objects. If the function returns different - object types depending on the parameter set provide one per parameter set. +#### Specify an OutputType attribute if the advanced function returns an object or collection of objects. + +If the function returns different object types depending on the parameter set provide one per parameter set. ```PowerShell [OutputType([], ParameterSetName="")] [OutputType("", ParameterSetName="")] ``` -#### When a ParameterSetName is used in any of the parameters, always provide a - DefaultParameterSetName in the CmdletBinding attribute. +#### When a ParameterSetName is used in any of the parameters, always provide a DefaultParameterSetName in the CmdletBinding attribute. ```PowerShell function Get-User { From fd30de2a0f6bea271f128d0811ba3d44f7b79f66 Mon Sep 17 00:00:00 2001 From: Mark Kraus Date: Wed, 3 May 2017 07:41:02 -0500 Subject: [PATCH 011/113] Update Introduction.md (link fix) ToC links include spaces and were not formatting as links. changed ' ' to '%20' do they will properly display as links. --- Best Practices/Introduction.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Best Practices/Introduction.md b/Best Practices/Introduction.md index 6657f3b..cd4ef8b 100644 --- a/Best Practices/Introduction.md +++ b/Best Practices/Introduction.md @@ -14,10 +14,10 @@ One final note about these Best Practices: the perspective of these guidelines h ## Table of Contents -- [Naming Conventions](Naming Conventions.md) -- [Building Reusable Tools](Building Reusable Tools.md) -- [Output and Formatting](Output and Formatting.md) -- [Error Handling](Error Handling.md) +- [Naming Conventions](Naming%20Conventions.md) +- [Building Reusable Tools](Building%20Reusable%20Tools.md) +- [Output and Formatting](Output%20and%20Formatting.md) +- [Error Handling](Error%20Handling.md) - [Performance](Performance.md) -- [Language, Interop and .Net](Language, Interop and .Net.md) -- [Metadata, Versioning, and Packaging](Metadata, Versioning, and Packaging.md) \ No newline at end of file +- [Language, Interop and .Net](Language,%20Interop%20and%20.Net.md) +- [Metadata, Versioning, and Packaging](Metadata,%20Versioning,%20and%20Packaging.md) From fd04e7176516b5ea8435252fbce133aeebfde419 Mon Sep 17 00:00:00 2001 From: Paul Broadwith Date: Tue, 20 Jun 2017 21:07:07 +0100 Subject: [PATCH 012/113] Updated TOC links TOC links had spaces in there which didn't render as links. Replaced them to allow the links to be rendered correctly. --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index fb580bb..d065c7b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

Creative Commons License

-This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License, please attribute to Don Jones, Matt Penny, Carlos Perez, Joel Bennett and the PowerShell Community. +This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License, please attribute to Don Jones, Matt Penny, Carlos Perez, Joel Bennett and the PowerShell Community. ###### You are free to: @@ -30,20 +30,20 @@ Having said that, remember: the points in the Best Practices documents and the S The guidelines are divided into these sections: -* [Style Guide (Introduction)](Style Guide/Introduction.md) - * [Code Layout and Formatting](Style Guide/Code Layout and Formatting.md) - * [Function Structure](Style Guide/Function Structure.md) - * [Documentation and Comments](Style Guide/Documentation and Comments.md) - * [Readability](Style Guide/Readability.md) - * [Naming Conventions](Style Guide/Naming Conventions.md) -* [Best Practices (Introduction)](Best Practices/Introduction.md) - * [Building Reusable Tools](Best Practices/Building Reusable Tools.md) - * [Output and Formatting](Best Practices/Output and Formatting.md) - * [Error Handling](Best Practices/Error Handling.md) - * [Performance](Best Practices/Performance.md) - * [Language, Interop and .Net](Best Practices/Language%2C Interop and .Net.md) - * [Naming Conventions](Best Practices/Naming Conventions.md) - * [Metadata, Versioning, and Packaging](Best Practices/Metadata%2C Versioning%2C and Packaging.md) +* [Style Guide (Introduction)](Style%20Guide/Introduction.md) + * [Code Layout and Formatting](Style%20Guide/Code%20Layout%20and%20Formatting.md) + * [Function Structure](Style%20Guide/Function%20Structure.md) + * [Documentation and Comments](Style%20Guide/Documentation%20and%20Comments.md) + * [Readability](Style%20Guide/Readability.md) + * [Naming Conventions](Style%20Guide/Naming%20Conventions.md) +* [Best Practices (Introduction)](Best%20Practices/Introduction.md) + * [Building Reusable Tools](Best%20Practices/Building%20Reusable%20Tools.md) + * [Output and Formatting](Best%20Practices/Output%20and%20Formatting.md) + * [Error Handling](Best%20Practices/Error%20Handling.md) + * [Performance](Best%20Practices/Performance.md) + * [Language, Interop and .Net](Best%20Practices/Language%2C%20Interop%20and%20.Net.md) + * [Naming Conventions](Best%20Practices/Naming%20Conventions.md) + * [Metadata, Versioning, and Packaging](Best%20Practices/Metadata%2C%20Versioning%2C%20and%20Packaging.md) ### Current State: From db2400127c4573667933b4cd5e0c97cd0bb99971 Mon Sep 17 00:00:00 2001 From: dandiddy Date: Mon, 10 Jul 2017 15:48:35 +0100 Subject: [PATCH 013/113] Fixing broken links --- Style Guide/Introduction.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Style Guide/Introduction.md b/Style Guide/Introduction.md index c79fac7..43d0e70 100644 --- a/Style Guide/Introduction.md +++ b/Style Guide/Introduction.md @@ -8,9 +8,9 @@ This document is an attempt to come to an agreement on a style-guide because we ## Table of Contents -- [Code Layout and Formatting](Code Layout and Formatting.md) -- [Function Structure](Function Structure.md) -- [Documentation and Comments](Documentation and Comments.md) +- [Code Layout and Formatting](Code%20Layout%20and%20Formatting.md) +- [Function Structure](Function%20Structure.md) +- [Documentation and Comments](Documentation%20and%20Comments.md) - [Readability](Readability.md) -- [Naming Conventions](Naming Conventions.md) +- [Naming Conventions](Naming%20Conventions.md) From 3f6a733e02aaa8fe59c83cb03802203f33c3cf9c Mon Sep 17 00:00:00 2001 From: agabrys Date: Tue, 18 Jul 2017 19:26:18 +0200 Subject: [PATCH 014/113] use as much as possible markdowns to display text/urls/quotes instead of html tags and fix urls --- CONTRIBUTING.md | 32 +++++++++++++++++--------------- LICENSE.md | 2 +- README.md | 4 ++-- TableOfContents.md | 30 +++++++++++++++--------------- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9db3ae4..719e032 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,20 +20,20 @@ The *PowerShell Best Practices* are always evolving, and continue to be edited a To repeat from the ReadMe, the guidelines are divided into these sections: -* Style Guide - * Code Layout and Formatting - * Function Structure - * Documentation and Commenting - * Readability - * Naming Conventions -* Best Practices - * Naming Conventions - * Building Reusable Tools - * Output and Formatting - * Error Handling - * Performance - * Language, Interop and .Net - * Metadata, Versioning, and Packaging +* [Style Guide (Introduction)](Style%20Guide/Introduction.md) + * [Code Layout and Formatting](Style%20Guide/Code%20Layout%20and%20Formatting.md) + * [Function Structure](Style%20Guide/Function%20Structure.md) + * [Documentation and Comments](Style%20Guide/Documentation%20and%20Comments.md) + * [Readability](Style%20Guide/Readability.md) + * [Naming Conventions](Style%20Guide/Naming%20Conventions.md) +* [Best Practices (Introduction)](Best%20Practices/Introduction.md) + * [Building Reusable Tools](Best%20Practices/Building%20Reusable%20Tools.md) + * [Output and Formatting](Best%20Practices/Output%20and%20Formatting.md) + * [Error Handling](Best%20Practices/Error%20Handling.md) + * [Performance](Best%20Practices/Performance.md) + * [Language, Interop and .Net](Best%20Practices/Language%2C%20Interop%20and%20.Net.md) + * [Naming Conventions](Best%20Practices/Naming%20Conventions.md) + * [Metadata, Versioning, and Packaging](Best%20Practices/Metadata%2C%20Versioning%2C%20and%20Packaging.md) Markdown documents on GitHub support linking within a document, but only to headers, so when editing, in addition to keeping practices and guidelines in the documents where they make sense, please use headlines for each guideline, and lower level headlines for rationale, examples, counter examples, and exceptions. @@ -43,4 +43,6 @@ In general, practices and guidelines should be at least a header with an explana Style guidelines in particular should be phrased as a prescriptive guideline telling people what to do rather than a proscriptive or prohibiting rule, and should have both examples and counter examples. -When you absolutely must write a negative rule, you should start with the phrase "avoid" and end with an "instead" sentence, like:
Avoid the use of `~` to represent the home folder.

The meaning of ~ is unfortunately dependent on the "current" provider at the time of execution. This isn't really a style issue, but it's an important rule for code you intend to share anyway. Instead, use `${Env:UserProfile}` or `(Get-PSProvider FileSystem).Home`

+When you absolutely must write a negative rule, you should start with the phrase "avoid" and end with an "instead" sentence, like: +> ###### Avoid the use of `~` to represent the home folder. +> The meaning of ~ is unfortunately dependent on the "current" provider at the time of execution. This isn't really a style issue, but it's an important rule for code you intend to share anyway. **Instead**, use `${Env:UserProfile}` or `(Get-PSProvider FileSystem).Home` diff --git a/LICENSE.md b/LICENSE.md index 33cfc29..31ba7a1 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -27,7 +27,7 @@ Portions copyright (c) Joel Bennett, 2015 #### NOTE -The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting https://github.com/PoshCode/PowerShellPracticeAndStyle +The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting [https://github.com/PoshCode/PowerShellPracticeAndStyle](https://github.com/PoshCode/PowerShellPracticeAndStyle) The *PowerShell Style Guide* in particular is in PREVIEW, and we are still actively working out our disagreements about the rules in the guide through the github issues system. Please don't be suprised if over then next few weeks we change rules to contradict what they say at this current moment. diff --git a/README.md b/README.md index d065c7b..a3a4e3d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

Creative Commons License

-This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License, please attribute to Don Jones, Matt Penny, Carlos Perez, Joel Bennett and the PowerShell Community. +This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/), please attribute to Don Jones, Matt Penny, Carlos Perez, Joel Bennett and the PowerShell Community. ###### You are free to: @@ -49,7 +49,7 @@ The guidelines are divided into these sections: Remember [what we mean by _Best Practices_](#what-are-best-practices) -The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting https://github.com/PoshCode/PowerShellPracticeAndStyle +The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting [https://github.com/PoshCode/PowerShellPracticeAndStyle](https://github.com/PoshCode/PowerShellPracticeAndStyle) The *PowerShell Style Guide* in particular is in PREVIEW, and we are still actively working out our disagreements about the rules in the guide through the github issues system. diff --git a/TableOfContents.md b/TableOfContents.md index ad0cd11..832f5ef 100644 --- a/TableOfContents.md +++ b/TableOfContents.md @@ -1,19 +1,19 @@ PowerShell Practice and Style Guide =================================== -[Introduction (ReadMe)](ReadMe.md) +[Introduction (ReadMe)](README.md) -* [Style Guide (Introduction)](Style Guide/Introduction.md) - * [Code Layout and Formatting](Style Guide/Code Layout and Formatting.md) - * [Function Structure](Style Guide/Function Structure.md) - * [Documentation and Comments](Style Guide/Documentation and Comments.md) - * [Readability](Style Guide/Readability.md) - * [Naming Conventions](Style Guide/Naming Conventions.md) -* [Best Practices (Introduction)](Best Practices/Introduction.md) - * [Building Reusable Tools](Best Practices/Building Reusable Tools.md) - * [Output and Formatting](Best Practices/Output and Formatting.md) - * [Error Handling](Best Practices/Error Handling.md) - * [Performance](Best Practices/Performance.md) - * [Language, Interop and .Net](Best Practices/Language%2C Interop and .Net.md) - * [Naming Conventions](Best Practices/Naming Conventions.md) - * [Metadata, Versioning, and Packaging](Best Practices/Metadata%2C Versioning%2C and Packaging.md) \ No newline at end of file +* [Style Guide (Introduction)](Style%20Guide/Introduction.md) + * [Code Layout and Formatting](Style%20Guide/Code%20Layout%20and%20Formatting.md) + * [Function Structure](Style%20Guide/Function%20Structure.md) + * [Documentation and Comments](Style%20Guide/Documentation%20and%20Comments.md) + * [Readability](Style%20Guide/Readability.md) + * [Naming Conventions](Style%20Guide/Naming%20Conventions.md) +* [Best Practices (Introduction)](Best%20Practices/Introduction.md) + * [Building Reusable Tools](Best%20Practices/Building%20Reusable%20Tools.md) + * [Output and Formatting](Best%20Practices/Output%20and%20Formatting.md) + * [Error Handling](Best%20Practices/Error%20Handling.md) + * [Performance](Best%20Practices/Performance.md) + * [Language, Interop and .Net](Best%20Practices/Language%2C%20Interop%20and%20.Net.md) + * [Naming Conventions](Best%20Practices/Naming%20Conventions.md) + * [Metadata, Versioning, and Packaging](Best%20Practices/Metadata%2C%20Versioning%2C%20and%20Packaging.md) \ No newline at end of file From af828362fb14cd46fa90368023376a00af96c310 Mon Sep 17 00:00:00 2001 From: agabrys Date: Tue, 18 Jul 2017 20:16:26 +0200 Subject: [PATCH 015/113] remove ignored MarkdownTOC, fix urls and add set #issues as links --- Style Guide/Code Layout and Formatting.md | 9 +++------ Style Guide/Readability.md | 2 +- Style Guide/TODO.md | 7 ------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/Style Guide/Code Layout and Formatting.md b/Style Guide/Code Layout and Formatting.md index a6e6be7..a802d63 100644 --- a/Style Guide/Code Layout and Formatting.md +++ b/Style Guide/Code Layout and Formatting.md @@ -1,6 +1,3 @@ - - - ### Code Layout & Formatting These guidelines are about readability. Some of them are arbitrary rules, but they are based on decades of traditions in programming, so while you may disagree with some rules (and should always follow the rules of individual projects), when we ask you to leave an empty line after a closing function brace, or two lines before functions, we're not being capricious, we're doing so because it makes it easier for experienced developers to scan your code. @@ -86,11 +83,11 @@ You can always delete or ignore one of the blocks (or add the `begin` block), ad #### Open braces on the same line Code folding is nicer in many editors. -(TODO: This is in discussion in #24) +(TODO: This is in discussion in [#24](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/24)) #### Closing braces always on their own line Because that's how they're supposed to be! -(TODO: This is in discussion in #24) +(TODO: This is in discussion in [#24](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/24)) #### Prefer: param() begin, process, end That's the order PowerShell will execute it in @@ -131,7 +128,7 @@ The PowerShell console is, by default, 120 characters wide, but it allows only 1 Most of us work on widescreen monitors these days, and there is little reason to keep a narrow line width, however, keeping files relatively narrow allows for side-by-side editing, so even narrower guidelines may be established by a given project. Be sure to check when you're working on someone else's project. -The preferred way to avoid long lines is to use splatting (see [About_Splatting](https://technet.microsoft.com/en-us/library/jj672955.aspx)) and PowerShell's implied line continuation inside parentheses, brackets, and braces -- these should always be used in preference to the backtick for line continuation when applicable, even for strings: +The preferred way to avoid long lines is to use splatting (see [About Splatting](https://technet.microsoft.com/en-us/library/jj672955.aspx)) and PowerShell's implied line continuation inside parentheses, brackets, and braces -- these should always be used in preference to the backtick for line continuation when applicable, even for strings: ``` Write-Host ("This is an incredibly important, and extremely long message. " + diff --git a/Style Guide/Readability.md b/Style Guide/Readability.md index cb6b6af..2e968c3 100644 --- a/Style Guide/Readability.md +++ b/Style Guide/Readability.md @@ -1,4 +1,4 @@ -TODO: This section should probably be merged to [Code Layout and Formatting](Code Layout and Formatting.md), and based on the #15, we should remove or rewrite the backticks section. +TODO: This section should probably be merged to [Code Layout and Formatting](Code%20Layout%20and%20Formatting.md), and based on the [#15](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/15), we should remove or rewrite the backticks section. # READ-01 Indent your code diff --git a/Style Guide/TODO.md b/Style Guide/TODO.md index 9b15aae..ed858bd 100644 --- a/Style Guide/TODO.md +++ b/Style Guide/TODO.md @@ -1,14 +1,7 @@ These documents are in an extremely rough state, not suitable for inclusion in the main guide yet. - - - [Include Help](#include-help) - - - - - ### Include Help Discuss: Add a blank line between comment based help and function declaration? From dd66c29d91b9b52d07d7f3ce487cce3acac21bae Mon Sep 17 00:00:00 2001 From: agabrys Date: Tue, 18 Jul 2017 20:52:29 +0200 Subject: [PATCH 016/113] fix dead and add missing urls --- Best Practices/Building Reusable Tools.md | 2 +- Best Practices/Introduction.md | 5 +++-- Best Practices/Output and Formatting.md | 4 ++-- Best Practices/TODO.md | 24 ----------------------- 4 files changed, 6 insertions(+), 29 deletions(-) diff --git a/Best Practices/Building Reusable Tools.md b/Best Practices/Building Reusable Tools.md index 893e693..ff3bacf 100644 --- a/Best Practices/Building Reusable Tools.md +++ b/Best Practices/Building Reusable Tools.md @@ -80,7 +80,7 @@ On the flip side, it's important to note that writing your own code from the gro # WAST-02 Report bugs to Microsoft -An exception: if you know that a built-in technique doesn't work properly (e.g., it is buggy or doesn't accomplish the exact task), then obviously it's fine to re-invent the wheel. However, in cases where you're doing so to avoid a bug or design flaw, then you should - as an upstanding member of the community - report the bug on Connect.Microsoft.com also. +An exception: if you know that a built-in technique doesn't work properly (e.g., it is buggy or doesn't accomplish the exact task), then obviously it's fine to re-invent the wheel. However, in cases where you're doing so to avoid a bug or design flaw, then you should - as an upstanding member of the community - report the bug on [connect.microsoft.com](http://connect.microsoft.com/) also. diff --git a/Best Practices/Introduction.md b/Best Practices/Introduction.md index cd4ef8b..10ee13d 100644 --- a/Best Practices/Introduction.md +++ b/Best Practices/Introduction.md @@ -2,9 +2,9 @@ ## Foreword -If you scan through code projects on [PoshCode](http://PoshCode.org) or the [Technet ScriptCenter Gallery](http://gallery.technet.microsoft.com/scriptcenter), it will be immediately apparent that people in the PowerShell community have vastly different ideas about what's "right and wrong" in the world of PowerShell scripting. +If you scan through code projects on [PoshCode](https://github.com/PoshCode) or the [Technet ScriptCenter Gallery](http://gallery.technet.microsoft.com/scriptcenter), it will be immediately apparent that people in the PowerShell community have vastly different ideas about what's "right and wrong" in the world of PowerShell scripting. -Over the years several attempts have been made to arrive at a consensus, most notably the "Great Debate" series of blog posts on [PowerShell.org](http://powershell.org/wp/category/great-debates/) following the 2013 Scripting Games, which outlined some of the more controversial issues and asked for community discussions. +Over the years several attempts have been made to arrive at a consensus, most notably the "Great Debate" series of blog posts on [PowerShell.org](https://powershell.org/?s=great+debate) following the 2013 Scripting Games, which outlined some of the more controversial issues and asked for community discussions. This project has been created, in part, as a public place to continue those debates as well as to document the consensus of the community when we _do_ arrive at a consensus. @@ -19,5 +19,6 @@ One final note about these Best Practices: the perspective of these guidelines h - [Output and Formatting](Output%20and%20Formatting.md) - [Error Handling](Error%20Handling.md) - [Performance](Performance.md) +- [Security](Security.md) - [Language, Interop and .Net](Language,%20Interop%20and%20.Net.md) - [Metadata, Versioning, and Packaging](Metadata,%20Versioning,%20and%20Packaging.md) diff --git a/Best Practices/Output and Formatting.md b/Best Practices/Output and Formatting.md index 115222f..bb46eb1 100644 --- a/Best Practices/Output and Formatting.md +++ b/Best Practices/Output and Formatting.md @@ -28,11 +28,11 @@ You should use the debug output stream for output that is useful for script debu ## Use CmdletBinding if you are using output streams -As we've already written elsewhere, you should probably [always use CmdletBinding](https://github.com/PoshCode/PowerShellPracticeAndStyle/blob/master/Style%20Guide/Code%20Layout%20and%20Formatting.md#always-write-cmdletbinding). +As we've already written elsewhere, you should probably [always use CmdletBinding](../Style%20Guide/Code%20Layout%20and%20Formatting.md#always-start-with-cmdletbinding). However, it's particularly important when you're using Write-Verbose and Write-Debug, as the Verbose and Debug output streams are off by default, and the `[CmdletBinding()]` attribute enables the common `-Verbose` and `-Debug` switches which turn those streams on. -It also enables the switches for the Warning and Error streams, as well as ways of collecting those streams into variables. You should read the [always use CmdletBinding](https://github.com/PoshCode/PowerShellPracticeAndStyle/blob/master/Style%20Guide/Code%20Layout%20and%20Formatting.md#always-write-cmdletbinding) topic for more information. +It also enables the switches for the Warning and Error streams, as well as ways of collecting those streams into variables. You should read the [always use CmdletBinding](../Style%20Guide/Code%20Layout%20and%20Formatting.md#always-start-with-cmdletbinding) topic for more information. ## Use Format Files for your custom objects diff --git a/Best Practices/TODO.md b/Best Practices/TODO.md index fae2840..fa42d17 100644 --- a/Best Practices/TODO.md +++ b/Best Practices/TODO.md @@ -1,7 +1,6 @@ These documents are in an extremely rough state, not suitable for inclusion in the main guide yet. ### Using The .Net Framework - - [Control what gets exported in a module](#control-what-gets-exported-in-a-module) - [Specify when to use a Manifest for a module](#specify-when-to-use-a-manifest-for-a-module) @@ -42,18 +41,10 @@ These documents are in an extremely rough state, not suitable for inclusion in t - [AVOID appending to arrays in a loop](#avoid-appending-to-arrays-in-a-loop) - [EXCEPTIONS:](#exceptions) - [RATIONALE:](#rationale) - - [AVOID appending to string in a loop](#avoid-appending-to-string-in-a-loop) - - [EXCEPTIONS:](#exceptions-1) - - [RATIONALE:](#rationale-1) - [Strongly type parameters](#strongly-type-parameters) - [Don't reinvent the wheel](#dont-reinvent-the-wheel) - [Let's talk about Logging](#lets-talk-about-logging) - [Let's talk about code signing](#lets-talk-about-code-signing) -- [Don't reinvent the wheel](#dont-reinvent-the-wheel-1) -- [Let's talk about Logging](#lets-talk-about-logging-1) -- [Let's talk about code signing](#lets-talk-about-code-signing-1) - - TODO @@ -282,18 +273,6 @@ The language features are always faster, and almost always more readable. Of cou ### You should understand the .Net underpinnings -#### AVOID appending to arrays in a loop -##### INSTEAD assign output from the loop -#### EXCEPTIONS: -* Appending to multiple collections -* Using Lists instead of arrays - -#### RATIONALE: -* Copying is slow -* Pipeline output uses ArrayList -* Easier to read and understand - - #### AVOID appending to string in a loop ##### INSTEAD assign output from the loop using $OFS #### EXCEPTIONS: @@ -317,9 +296,6 @@ When passing on parameters to another command, you should be _at least_ as stron One notable exception is when you could accept more than one type. In PowerShell you can speficy parameter set overloads, but you can't change the type of a parameter. -### Don't reinvent the wheel -### Let's talk about Logging -### Let's talk about code signing ### Don't reinvent the wheel ### Let's talk about Logging ### Let's talk about code signing \ No newline at end of file From df08531bb8787b67bee41b68de5b894065fd60eb Mon Sep 17 00:00:00 2001 From: agabrys Date: Tue, 18 Jul 2017 21:04:38 +0200 Subject: [PATCH 017/113] add missing Best Practices/Security section + set the same order of Best Practices subsections --- CONTRIBUTING.md | 3 ++- README.md | 3 ++- TableOfContents.md | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 719e032..61d4032 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,12 +27,13 @@ To repeat from the ReadMe, the guidelines are divided into these sections: * [Readability](Style%20Guide/Readability.md) * [Naming Conventions](Style%20Guide/Naming%20Conventions.md) * [Best Practices (Introduction)](Best%20Practices/Introduction.md) + * [Naming Conventions](Best%20Practices/Naming%20Conventions.md) * [Building Reusable Tools](Best%20Practices/Building%20Reusable%20Tools.md) * [Output and Formatting](Best%20Practices/Output%20and%20Formatting.md) * [Error Handling](Best%20Practices/Error%20Handling.md) * [Performance](Best%20Practices/Performance.md) + * [Security](Best%20Practices/Security.md) * [Language, Interop and .Net](Best%20Practices/Language%2C%20Interop%20and%20.Net.md) - * [Naming Conventions](Best%20Practices/Naming%20Conventions.md) * [Metadata, Versioning, and Packaging](Best%20Practices/Metadata%2C%20Versioning%2C%20and%20Packaging.md) Markdown documents on GitHub support linking within a document, but only to headers, so when editing, in addition to keeping practices and guidelines in the documents where they make sense, please use headlines for each guideline, and lower level headlines for rationale, examples, counter examples, and exceptions. diff --git a/README.md b/README.md index a3a4e3d..23bddf3 100644 --- a/README.md +++ b/README.md @@ -37,12 +37,13 @@ The guidelines are divided into these sections: * [Readability](Style%20Guide/Readability.md) * [Naming Conventions](Style%20Guide/Naming%20Conventions.md) * [Best Practices (Introduction)](Best%20Practices/Introduction.md) + * [Naming Conventions](Best%20Practices/Naming%20Conventions.md) * [Building Reusable Tools](Best%20Practices/Building%20Reusable%20Tools.md) * [Output and Formatting](Best%20Practices/Output%20and%20Formatting.md) * [Error Handling](Best%20Practices/Error%20Handling.md) * [Performance](Best%20Practices/Performance.md) + * [Security](Best%20Practices/Security.md) * [Language, Interop and .Net](Best%20Practices/Language%2C%20Interop%20and%20.Net.md) - * [Naming Conventions](Best%20Practices/Naming%20Conventions.md) * [Metadata, Versioning, and Packaging](Best%20Practices/Metadata%2C%20Versioning%2C%20and%20Packaging.md) ### Current State: diff --git a/TableOfContents.md b/TableOfContents.md index 832f5ef..e994024 100644 --- a/TableOfContents.md +++ b/TableOfContents.md @@ -10,10 +10,11 @@ PowerShell Practice and Style Guide * [Readability](Style%20Guide/Readability.md) * [Naming Conventions](Style%20Guide/Naming%20Conventions.md) * [Best Practices (Introduction)](Best%20Practices/Introduction.md) + * [Naming Conventions](Best%20Practices/Naming%20Conventions.md) * [Building Reusable Tools](Best%20Practices/Building%20Reusable%20Tools.md) * [Output and Formatting](Best%20Practices/Output%20and%20Formatting.md) * [Error Handling](Best%20Practices/Error%20Handling.md) * [Performance](Best%20Practices/Performance.md) + * [Security](Best%20Practices/Security.md) * [Language, Interop and .Net](Best%20Practices/Language%2C%20Interop%20and%20.Net.md) - * [Naming Conventions](Best%20Practices/Naming%20Conventions.md) * [Metadata, Versioning, and Packaging](Best%20Practices/Metadata%2C%20Versioning%2C%20and%20Packaging.md) \ No newline at end of file From bb908f286862e0ff66b975a3d861f554d0115ee0 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 18 Jul 2017 17:45:41 -0400 Subject: [PATCH 018/113] Add a book.json for gitbook Tell them to use our TableOfContents --- book.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 book.json diff --git a/book.json b/book.json new file mode 100644 index 0000000..4a44eee --- /dev/null +++ b/book.json @@ -0,0 +1,5 @@ +{ + "structure": { + "summary": "TableOfContents.md" + } +} \ No newline at end of file From 7c7ac4d9c86414fab17a16e1b545744e4148b5f9 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 18 Jul 2017 17:47:46 -0400 Subject: [PATCH 019/113] Breaking Change: use dashes in file names --- .../Building-Reusable-Tools.md | 0 .../Error-Handling.md | 140 +++++++++--------- .../Introduction.md | 0 .../Language,-Interop-and-.Net.md | 0 .../Metadata,-Versioning,-and-Packaging.md | 0 .../Naming-Conventions.md | 0 .../Output-and-Formatting.md | 0 .../Performance.md | 0 .../Security.md | 0 {Best Practices => Best-Practices}/TODO.md | 0 .../Code-Layout-and-Formatting.md | 0 .../Documentation-and-Comments.md | 0 .../Function-Structure.md | 0 {Style Guide => Style-Guide}/Introduction.md | 0 .../Naming-Conventions.md | 0 {Style Guide => Style-Guide}/Readability.md | 0 {Style Guide => Style-Guide}/TODO.md | 0 17 files changed, 70 insertions(+), 70 deletions(-) rename Best Practices/Building Reusable Tools.md => Best-Practices/Building-Reusable-Tools.md (100%) rename Best Practices/Error Handling.md => Best-Practices/Error-Handling.md (97%) rename {Best Practices => Best-Practices}/Introduction.md (100%) rename Best Practices/Language, Interop and .Net.md => Best-Practices/Language,-Interop-and-.Net.md (100%) rename Best Practices/Metadata, Versioning, and Packaging.md => Best-Practices/Metadata,-Versioning,-and-Packaging.md (100%) rename Best Practices/Naming Conventions.md => Best-Practices/Naming-Conventions.md (100%) rename Best Practices/Output and Formatting.md => Best-Practices/Output-and-Formatting.md (100%) rename {Best Practices => Best-Practices}/Performance.md (100%) rename {Best Practices => Best-Practices}/Security.md (100%) rename {Best Practices => Best-Practices}/TODO.md (100%) rename Style Guide/Code Layout and Formatting.md => Style-Guide/Code-Layout-and-Formatting.md (100%) rename Style Guide/Documentation and Comments.md => Style-Guide/Documentation-and-Comments.md (100%) rename Style Guide/Function Structure.md => Style-Guide/Function-Structure.md (100%) rename {Style Guide => Style-Guide}/Introduction.md (100%) rename Style Guide/Naming Conventions.md => Style-Guide/Naming-Conventions.md (100%) rename {Style Guide => Style-Guide}/Readability.md (100%) rename {Style Guide => Style-Guide}/TODO.md (100%) diff --git a/Best Practices/Building Reusable Tools.md b/Best-Practices/Building-Reusable-Tools.md similarity index 100% rename from Best Practices/Building Reusable Tools.md rename to Best-Practices/Building-Reusable-Tools.md diff --git a/Best Practices/Error Handling.md b/Best-Practices/Error-Handling.md similarity index 97% rename from Best Practices/Error Handling.md rename to Best-Practices/Error-Handling.md index b9de486..3461775 100644 --- a/Best Practices/Error Handling.md +++ b/Best-Practices/Error-Handling.md @@ -1,70 +1,70 @@ -# ERR-01 Use -ErrorAction Stop when calling cmdlets - -When trapping an error, try to use -ErrorAction Stop on cmdlets to generate terminating, trappable exceptions. - -# ERR-02 Use $ErrorActionPreference='Stop' or 'Continue' when calling non-cmdlets - -When executing something other than a cmdlet, set $ErrorActionPreference='Stop' before executing, and re-set to Continue afterwards. If you're concerned about using -ErrorAction because it will bail on the entire pipeline, then you've probably over-constructed the pipeline. Consider using a more scripting-construct-style approach, because those approaches are inherently better for automated error handling. - -Ideally, whatever command or code you think might bomb should be dealing with one thing: querying one computer, deleting one file, updating one user. That way, if an error occurs, you can handle it and then get on with the next thing. - -# ERR-03 Avoid using flags to handle errors - -Try to avoid setting flags: - -```PowerShell -try { - $continue = $true - Do-Something -ErrorAction Stop -} catch { - $continue = $false -} - -if ($continue) { - Do-This - Set-That - Get-Those -} -``` - -Instead, put the entire "transaction" into the Try block: - -```PowerShell -try { - Do-Something -ErrorAction Stop - Do-This - Set-That - Get-Those -} catch { - Handle-Error -} -``` - -It's a lot easier to follow the logic. - -# ERR-04 Avoid using $? - -When you need to examine the error that occurred, try to avoid using $?. It actually doesn't mean an error did or did not occur; it's reporting whether or not the last-run command considered itself to have completed successfully. You get no details on what happened. - - -# ERR-05 Avoid testing for a null variable as an error condition - -Also try to avoid testing for a null variable as an error condition: - -``` -$user = Get-ADUser -Identity DonJ - -if ($user) { - $user | Do-Something -} else { - Write-Warning "Could not get user $user" -} -``` - -There are times and technologies where that's the only approach that will work, especially if the command you're running won't produce a terminating, trappable exception. But it's a logically contorted approach, and it can make debugging trickier. - -# ERR-06 Copy $Error[0] to your own variable - -Within a `catch` block, `$_` will contain the last error that occurred, as will `$Error[0]`. Use either - but immediately copy them into your own variable, as executing additional commands can cause `$_` to get "hijacked" or `$Error[0]` to contain a different error. - -It isn't necessary to clear `$Error` in most cases. `$Error[0]` will be the last error, and PowerShell will maintain the rest of the `$Error` collection automatically. +# ERR-01 Use -ErrorAction Stop when calling cmdlets + +When trapping an error, try to use -ErrorAction Stop on cmdlets to generate terminating, trappable exceptions. + +# ERR-02 Use $ErrorActionPreference='Stop' or 'Continue' when calling non-cmdlets + +When executing something other than a cmdlet, set $ErrorActionPreference='Stop' before executing, and re-set to Continue afterwards. If you're concerned about using -ErrorAction because it will bail on the entire pipeline, then you've probably over-constructed the pipeline. Consider using a more scripting-construct-style approach, because those approaches are inherently better for automated error handling. + +Ideally, whatever command or code you think might bomb should be dealing with one thing: querying one computer, deleting one file, updating one user. That way, if an error occurs, you can handle it and then get on with the next thing. + +# ERR-03 Avoid using flags to handle errors + +Try to avoid setting flags: + +```PowerShell +try { + $continue = $true + Do-Something -ErrorAction Stop +} catch { + $continue = $false +} + +if ($continue) { + Do-This + Set-That + Get-Those +} +``` + +Instead, put the entire "transaction" into the Try block: + +```PowerShell +try { + Do-Something -ErrorAction Stop + Do-This + Set-That + Get-Those +} catch { + Handle-Error +} +``` + +It's a lot easier to follow the logic. + +# ERR-04 Avoid using $? + +When you need to examine the error that occurred, try to avoid using $?. It actually doesn't mean an error did or did not occur; it's reporting whether or not the last-run command considered itself to have completed successfully. You get no details on what happened. + + +# ERR-05 Avoid testing for a null variable as an error condition + +Also try to avoid testing for a null variable as an error condition: + +``` +$user = Get-ADUser -Identity DonJ + +if ($user) { + $user | Do-Something +} else { + Write-Warning "Could not get user $user" +} +``` + +There are times and technologies where that's the only approach that will work, especially if the command you're running won't produce a terminating, trappable exception. But it's a logically contorted approach, and it can make debugging trickier. + +# ERR-06 Copy $Error[0] to your own variable + +Within a `catch` block, `$_` will contain the last error that occurred, as will `$Error[0]`. Use either - but immediately copy them into your own variable, as executing additional commands can cause `$_` to get "hijacked" or `$Error[0]` to contain a different error. + +It isn't necessary to clear `$Error` in most cases. `$Error[0]` will be the last error, and PowerShell will maintain the rest of the `$Error` collection automatically. diff --git a/Best Practices/Introduction.md b/Best-Practices/Introduction.md similarity index 100% rename from Best Practices/Introduction.md rename to Best-Practices/Introduction.md diff --git a/Best Practices/Language, Interop and .Net.md b/Best-Practices/Language,-Interop-and-.Net.md similarity index 100% rename from Best Practices/Language, Interop and .Net.md rename to Best-Practices/Language,-Interop-and-.Net.md diff --git a/Best Practices/Metadata, Versioning, and Packaging.md b/Best-Practices/Metadata,-Versioning,-and-Packaging.md similarity index 100% rename from Best Practices/Metadata, Versioning, and Packaging.md rename to Best-Practices/Metadata,-Versioning,-and-Packaging.md diff --git a/Best Practices/Naming Conventions.md b/Best-Practices/Naming-Conventions.md similarity index 100% rename from Best Practices/Naming Conventions.md rename to Best-Practices/Naming-Conventions.md diff --git a/Best Practices/Output and Formatting.md b/Best-Practices/Output-and-Formatting.md similarity index 100% rename from Best Practices/Output and Formatting.md rename to Best-Practices/Output-and-Formatting.md diff --git a/Best Practices/Performance.md b/Best-Practices/Performance.md similarity index 100% rename from Best Practices/Performance.md rename to Best-Practices/Performance.md diff --git a/Best Practices/Security.md b/Best-Practices/Security.md similarity index 100% rename from Best Practices/Security.md rename to Best-Practices/Security.md diff --git a/Best Practices/TODO.md b/Best-Practices/TODO.md similarity index 100% rename from Best Practices/TODO.md rename to Best-Practices/TODO.md diff --git a/Style Guide/Code Layout and Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md similarity index 100% rename from Style Guide/Code Layout and Formatting.md rename to Style-Guide/Code-Layout-and-Formatting.md diff --git a/Style Guide/Documentation and Comments.md b/Style-Guide/Documentation-and-Comments.md similarity index 100% rename from Style Guide/Documentation and Comments.md rename to Style-Guide/Documentation-and-Comments.md diff --git a/Style Guide/Function Structure.md b/Style-Guide/Function-Structure.md similarity index 100% rename from Style Guide/Function Structure.md rename to Style-Guide/Function-Structure.md diff --git a/Style Guide/Introduction.md b/Style-Guide/Introduction.md similarity index 100% rename from Style Guide/Introduction.md rename to Style-Guide/Introduction.md diff --git a/Style Guide/Naming Conventions.md b/Style-Guide/Naming-Conventions.md similarity index 100% rename from Style Guide/Naming Conventions.md rename to Style-Guide/Naming-Conventions.md diff --git a/Style Guide/Readability.md b/Style-Guide/Readability.md similarity index 100% rename from Style Guide/Readability.md rename to Style-Guide/Readability.md diff --git a/Style Guide/TODO.md b/Style-Guide/TODO.md similarity index 100% rename from Style Guide/TODO.md rename to Style-Guide/TODO.md From cff520e440d5eb864620459ed688308fb853fcdf Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 18 Jul 2017 17:51:37 -0400 Subject: [PATCH 020/113] Fix Links --- ...d-.Net.md => Language-Interop-and-.Net.md} | 0 ...d => Metadata-Versioning-and-Packaging.md} | 0 TableOfContents.md | 35 +++++++++++-------- 3 files changed, 20 insertions(+), 15 deletions(-) rename Best-Practices/{Language,-Interop-and-.Net.md => Language-Interop-and-.Net.md} (100%) rename Best-Practices/{Metadata,-Versioning,-and-Packaging.md => Metadata-Versioning-and-Packaging.md} (100%) diff --git a/Best-Practices/Language,-Interop-and-.Net.md b/Best-Practices/Language-Interop-and-.Net.md similarity index 100% rename from Best-Practices/Language,-Interop-and-.Net.md rename to Best-Practices/Language-Interop-and-.Net.md diff --git a/Best-Practices/Metadata,-Versioning,-and-Packaging.md b/Best-Practices/Metadata-Versioning-and-Packaging.md similarity index 100% rename from Best-Practices/Metadata,-Versioning,-and-Packaging.md rename to Best-Practices/Metadata-Versioning-and-Packaging.md diff --git a/TableOfContents.md b/TableOfContents.md index ad0cd11..f89e248 100644 --- a/TableOfContents.md +++ b/TableOfContents.md @@ -1,19 +1,24 @@ PowerShell Practice and Style Guide =================================== -[Introduction (ReadMe)](ReadMe.md) +[Introduction](ReadMe.md) -* [Style Guide (Introduction)](Style Guide/Introduction.md) - * [Code Layout and Formatting](Style Guide/Code Layout and Formatting.md) - * [Function Structure](Style Guide/Function Structure.md) - * [Documentation and Comments](Style Guide/Documentation and Comments.md) - * [Readability](Style Guide/Readability.md) - * [Naming Conventions](Style Guide/Naming Conventions.md) -* [Best Practices (Introduction)](Best Practices/Introduction.md) - * [Building Reusable Tools](Best Practices/Building Reusable Tools.md) - * [Output and Formatting](Best Practices/Output and Formatting.md) - * [Error Handling](Best Practices/Error Handling.md) - * [Performance](Best Practices/Performance.md) - * [Language, Interop and .Net](Best Practices/Language%2C Interop and .Net.md) - * [Naming Conventions](Best Practices/Naming Conventions.md) - * [Metadata, Versioning, and Packaging](Best Practices/Metadata%2C Versioning%2C and Packaging.md) \ No newline at end of file +## Style Guide + +* [Introduction](Style-Guide/Introduction.md) +* [Code Layout and Formatting](Style-Guide/Code-Layout-and-Formatting.md) +* [Function Structure](Style-Guide/Function-Structure.md) +* [Documentation and Comments](Style-Guide/Documentation-and-Comments.md) +* [Readability](Style-Guide/Readability.md) +* [Naming Conventions](Style-Guide/Naming-Conventions.md) + +## Best Practices + +* [Introduction](Best-Practices/Introduction.md) +* [Building Reusable Tools](Best-Practices/Building-Reusable-Tools.md) +* [Output and Formatting](Best-Practices/Output-and-Formatting.md) +* [Error Handling](Best-Practices/Error-Handling.md) +* [Performance](Best-Practices/Performance.md) +* [Language, Interop and .Net](Best-Practices/Language-Interop-and-.Net.md) +* [Naming Conventions](Best-Practices/Naming-Conventions.md) +* [Metadata, Versioning, and Packaging](Best-Practices/Metadata-Versioning-and-Packaging.md) \ No newline at end of file From d92fd512fffa8293c71094bd558620d26569fa93 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 18 Jul 2017 18:06:51 -0400 Subject: [PATCH 021/113] Fix links broken by rename --- Best-Practices/Introduction.md | 14 +++++------ Best-Practices/Output-and-Formatting.md | 8 +++---- README.md | 32 ++++++++++++------------- Style-Guide/Introduction.md | 10 ++++---- Style-Guide/Readability.md | 2 +- TableOfContents.md | 4 +++- 6 files changed, 36 insertions(+), 34 deletions(-) diff --git a/Best-Practices/Introduction.md b/Best-Practices/Introduction.md index cd4ef8b..e7e3dd4 100644 --- a/Best-Practices/Introduction.md +++ b/Best-Practices/Introduction.md @@ -6,7 +6,7 @@ If you scan through code projects on [PoshCode](http://PoshCode.org) or the [Tec Over the years several attempts have been made to arrive at a consensus, most notably the "Great Debate" series of blog posts on [PowerShell.org](http://powershell.org/wp/category/great-debates/) following the 2013 Scripting Games, which outlined some of the more controversial issues and asked for community discussions. -This project has been created, in part, as a public place to continue those debates as well as to document the consensus of the community when we _do_ arrive at a consensus. +This project has been created, in part, as a public place to continue those debates as well as to document the consensus of the community when we _do_ arrive at a consensus. Remember that best practices are not hard-and-fast rules. We don't even consider them as solid as the style guide rules. They are the things you should _usually_ do as a starting point, and should deviate from when it's appropriate. @@ -14,10 +14,10 @@ One final note about these Best Practices: the perspective of these guidelines h ## Table of Contents -- [Naming Conventions](Naming%20Conventions.md) -- [Building Reusable Tools](Building%20Reusable%20Tools.md) -- [Output and Formatting](Output%20and%20Formatting.md) -- [Error Handling](Error%20Handling.md) +- [Naming Conventions](Naming-Conventions.md) +- [Building Reusable Tools](Building-Reusable-Tools.md) +- [Output and Formatting](Output-and-Formatting.md) +- [Error Handling](Error-Handling.md) - [Performance](Performance.md) -- [Language, Interop and .Net](Language,%20Interop%20and%20.Net.md) -- [Metadata, Versioning, and Packaging](Metadata,%20Versioning,%20and%20Packaging.md) +- [Language, Interop and .Net](Language,-Interop-and-.Net.md) +- [Metadata, Versioning, and Packaging](Metadata,-Versioning,-and-Packaging.md) diff --git a/Best-Practices/Output-and-Formatting.md b/Best-Practices/Output-and-Formatting.md index 115222f..8a0d5c8 100644 --- a/Best-Practices/Output-and-Formatting.md +++ b/Best-Practices/Output-and-Formatting.md @@ -6,7 +6,7 @@ TODO: This whole document is STILL ROUGH DRAFT Previous to PowerShell 5, Write-Host has no functionality at all in non-interactive scripts. It cannot be captured or redirected, and therefore should only be used in functions which are "Show"ing or "Format"ing output, or to display something as part of an interactive prompt to the user. -That is: you should not use Write-Host to create script output unless your script (or function, or whatever) uses the Show verb (as in, Show-Performance) or the Format verb (as in, Format-Hex), or has a `-Formatted` switch parameter. You may also use it to build a interactions with the user in other cases (e.g. to write extra information to the screen before prompting the user for a choice or input). +That is: you should not use Write-Host to create script output unless your script (or function, or whatever) uses the Show verb (as in, Show-Performance) or the Format verb (as in, Format-Hex), or has a `-Formatted` switch parameter. You may also use it to build a interactions with the user in other cases (e.g. to write extra information to the screen before prompting the user for a choice or input). Generally, you should consider the other Write-* commands first when trying to give information to the user. @@ -28,11 +28,11 @@ You should use the debug output stream for output that is useful for script debu ## Use CmdletBinding if you are using output streams -As we've already written elsewhere, you should probably [always use CmdletBinding](https://github.com/PoshCode/PowerShellPracticeAndStyle/blob/master/Style%20Guide/Code%20Layout%20and%20Formatting.md#always-write-cmdletbinding). +As we've already written elsewhere, you should probably [always use CmdletBinding](https://github.com/PoshCode/PowerShellPracticeAndStyle/blob/master/Style-Guide/Code-Layout-and-Formatting.md#always-write-cmdletbinding). -However, it's particularly important when you're using Write-Verbose and Write-Debug, as the Verbose and Debug output streams are off by default, and the `[CmdletBinding()]` attribute enables the common `-Verbose` and `-Debug` switches which turn those streams on. +However, it's particularly important when you're using Write-Verbose and Write-Debug, as the Verbose and Debug output streams are off by default, and the `[CmdletBinding()]` attribute enables the common `-Verbose` and `-Debug` switches which turn those streams on. -It also enables the switches for the Warning and Error streams, as well as ways of collecting those streams into variables. You should read the [always use CmdletBinding](https://github.com/PoshCode/PowerShellPracticeAndStyle/blob/master/Style%20Guide/Code%20Layout%20and%20Formatting.md#always-write-cmdletbinding) topic for more information. +It also enables the switches for the Warning and Error streams, as well as ways of collecting those streams into variables. You should read the [always use CmdletBinding](https://github.com/PoshCode/PowerShellPracticeAndStyle/blob/master/Style-Guide/Code-Layout-and-Formatting.md#always-write-cmdletbinding) topic for more information. ## Use Format Files for your custom objects diff --git a/README.md b/README.md index d065c7b..766e2e2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This work is licensed under a Date: Tue, 18 Jul 2017 18:17:30 -0400 Subject: [PATCH 022/113] Attempt to fix gitbook TOC --- TableOfContents.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/TableOfContents.md b/TableOfContents.md index 8f8ff58..4bf2ab3 100644 --- a/TableOfContents.md +++ b/TableOfContents.md @@ -3,8 +3,6 @@ PowerShell Practice and Style Guide ## Introduction -* [About this Guide](ReadMe.md) - ## Style Guide * [Introduction](Style-Guide/Introduction.md) From bd2ce47adbc314f455cb0ad130c4fe7a9c6096a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Gabry=C5=9B?= Date: Wed, 19 Jul 2017 08:32:04 +0200 Subject: [PATCH 023/113] add ReadMe to TableOfContents --- TableOfContents.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TableOfContents.md b/TableOfContents.md index 8a35e6d..c0a23ad 100644 --- a/TableOfContents.md +++ b/TableOfContents.md @@ -2,6 +2,7 @@ PowerShell Practice and Style Guide =================================== ## Introduction +* [About this Guide](README.md) ## Style Guide From 6a2417333b6aaee13e268c66b72c3af7ea6eed36 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Thu, 20 Jul 2017 00:52:24 -0400 Subject: [PATCH 024/113] Add a Contributing link --- TableOfContents.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TableOfContents.md b/TableOfContents.md index c0a23ad..2798faa 100644 --- a/TableOfContents.md +++ b/TableOfContents.md @@ -3,6 +3,7 @@ PowerShell Practice and Style Guide ## Introduction * [About this Guide](README.md) +* [Contributing](CONTRIBUTING.md) ## Style Guide From dc172e652ef7f0c0d72f2c9ee8145c2c62811296 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Thu, 20 Jul 2017 00:53:36 -0400 Subject: [PATCH 025/113] Remove commas from links --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b1549c2..4751222 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,8 +33,8 @@ To repeat from the ReadMe, the guidelines are divided into these sections: * [Error Handling](Best-Practices/Error-Handling.md) * [Performance](Best-Practices/Performance.md) * [Security](Best-Practices/Security.md) - * [Language, Interop and .Net](Best-Practices/Language%2C-Interop-and-.Net.md) - * [Metadata, Versioning, and Packaging](Best-Practices/Metadata%2C-Versioning%2C-and-Packaging.md) + * [Language, Interop and .Net](Best-Practices/Language-Interop-and-.Net.md) + * [Metadata, Versioning, and Packaging](Best-Practices/Metadata-Versioning-and-Packaging.md) Markdown documents on GitHub support linking within a document, but only to headers, so when editing, in addition to keeping practices and guidelines in the documents where they make sense, please use headlines for each guideline, and lower level headlines for rationale, examples, counter examples, and exceptions. From 61547186501a9aeaf2c7b710683948af74c2594d Mon Sep 17 00:00:00 2001 From: David Zampino Date: Thu, 20 Jul 2017 12:29:09 -0500 Subject: [PATCH 026/113] Correct semi-colons to colons --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index a802d63..784a7a4 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -156,7 +156,7 @@ Lines should not have trailing whitespace. Extra spaces result in future edits w You should use a single space around parameter names and operators, including comparison operators and math and assignment operators, even when the spaces are not necessary for PowerShell to correctly parse the code. -One notable exception is when using semi-colons to pass values to switch parameters: +One notable exception is when using colons to pass values to switch parameters: ```PowerShell # Do not write: From 70b12e18cc736873fb5e5d7bab272bcb74344cf6 Mon Sep 17 00:00:00 2001 From: David Zampino Date: Thu, 20 Jul 2017 12:29:32 -0500 Subject: [PATCH 027/113] Change help parameters to uppercase for consistency --- Style-Guide/Documentation-and-Comments.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index 8122d44..1442786 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -30,9 +30,9 @@ If the block is particularly long (as in the case of documentation text) it is r # Writing comments one-per line makes them stand out more in the console. <# - .Synopsis + .SYNOPSIS Really long comment blocks are tedious to keep commented in single-line mode - .Description + .DESCRIPTION Particularly when the comment must be frequently edited, as with the help and documentation for a function or script #> @@ -80,7 +80,7 @@ Every script function command should have at least a short statement describing Each parameter should be documented. To make it easier to keep the comments synchronized with changes to the parameters, the parameter documentation comments may _within_ the `param` block, directly above each parameter. -It is also possible to write `.Parameter` statements with the rest of the documentation comments, but they will be less likely to be left un-updated if you put them closer to the actual code they document. +It is also possible to write `.PARAMETER` statements with the rest of the documentation comments, but they will be less likely to be left un-updated if you put them closer to the actual code they document. ##### Provide Usage Examples @@ -90,9 +90,9 @@ Your help should always provide an example for each major use case. A 'usage exa ```PowerShell function Test-Help { <# - .Synopsis + .SYNOPSIS An example function to display how help should be written - .Example + .EXAMPLE Get-Help -Name Test-Help This shows the help for the example function From b1eb51dab38b07abe1cf068fcc8f026f99273a1b Mon Sep 17 00:00:00 2001 From: agabrys Date: Thu, 20 Jul 2017 19:43:19 +0200 Subject: [PATCH 028/113] fix broken links (commas have been removed in previous commits) --- Best-Practices/Introduction.md | 4 ++-- README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Best-Practices/Introduction.md b/Best-Practices/Introduction.md index 997acce..b32ef39 100644 --- a/Best-Practices/Introduction.md +++ b/Best-Practices/Introduction.md @@ -20,5 +20,5 @@ One final note about these Best Practices: the perspective of these guidelines h - [Error Handling](Error-Handling.md) - [Performance](Performance.md) - [Security](Security.md) -- [Language, Interop and .Net](Language,-Interop-and-.Net.md) -- [Metadata, Versioning, and Packaging](Metadata,-Versioning,-and-Packaging.md) +- [Language, Interop and .Net](Language-Interop-and-.Net.md) +- [Metadata, Versioning, and Packaging](Metadata-Versioning-and-Packaging.md) diff --git a/README.md b/README.md index d54844a..bffd698 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ The guidelines are divided into these sections: * [Error Handling](Best-Practices/Error-Handling.md) * [Performance](Best-Practices/Performance.md) * [Security](Best-Practices/Security.md) - * [Language, Interop and .Net](Best-Practices/Language%2C-Interop-and-.Net.md) - * [Metadata, Versioning, and Packaging](Best-Practices/Metadata%2C-Versioning%2C-and-Packaging.md) + * [Language, Interop and .Net](Best-Practices/Language-Interop-and-.Net.md) + * [Metadata, Versioning, and Packaging](Best-Practices/Metadata-Versioning-and-Packaging.md) ### Current State: From c565cbd23d3223b485d085275ff6fcbbcd0f66ad Mon Sep 17 00:00:00 2001 From: Jaco de Weerd Date: Thu, 17 Aug 2017 14:26:52 +0200 Subject: [PATCH 029/113] Adding more parameter documentation Suggestions to add and improve parameter documentation. --- Style-Guide/Documentation-and-Comments.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index 1442786..9b6405c 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -78,7 +78,22 @@ Every script function command should have at least a short statement describing ##### Document Each Parameter -Each parameter should be documented. To make it easier to keep the comments synchronized with changes to the parameters, the parameter documentation comments may _within_ the `param` block, directly above each parameter. +Each parameter should be documented. To make it easier to keep the comments synchronized with changes to the parameters, the preferred location for parameter documentation comments is _within_ the `param` block, directly above each parameter. +Examples can be found in the ISE snippets: + +```powershell +Param( + # Param1 help description + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + Position=0)] + $Param1, + +    # Param2 help description + [int] + $Param2 +) +``` It is also possible to write `.PARAMETER` statements with the rest of the documentation comments, but they will be less likely to be left un-updated if you put them closer to the actual code they document. @@ -130,6 +145,10 @@ function get-example { .PARAMETER FirstParameter Description of each of the parameters + Note: +        To make it easier to keep the comments synchronized with changes to the parameters, + the preferred location for parameter documentation comments is not here, + but within the param block, directly above each parameter. .PARAMETER SecondParameter Description of each of the parameters From 2fd1a95612be395ffae6216ebaeabcc1c72afb9d Mon Sep 17 00:00:00 2001 From: Thomas Rayner Date: Tue, 3 Oct 2017 15:36:27 -0600 Subject: [PATCH 030/113] Updated some grammar and capitalization Because Hacktoberfest... and contributing to community standards is good :) --- Best-Practices/Output-and-Formatting.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Best-Practices/Output-and-Formatting.md b/Best-Practices/Output-and-Formatting.md index a5da34d..702fa5a 100644 --- a/Best-Practices/Output-and-Formatting.md +++ b/Best-Practices/Output-and-Formatting.md @@ -2,7 +2,7 @@ TODO: This whole document is STILL ROUGH DRAFT -## Don't use write-host unless you really mean it +## Don't use Write-Host unless you really mean it Previous to PowerShell 5, Write-Host has no functionality at all in non-interactive scripts. It cannot be captured or redirected, and therefore should only be used in functions which are "Show"ing or "Format"ing output, or to display something as part of an interactive prompt to the user. @@ -14,7 +14,7 @@ Generally, you should consider the other Write-* commands first when trying to g When you're letting the user know how far through the script they are, or just making sure they know that _something_ is happening, Write-Progress is the right command to use. In the case of graphical hosts or remote jobs, this output can be shown to the user in real time, even when verbose and other streams are being collected and logged. -However, progress output is ephemeral -- it doesn't stick around, and you should not put anything exclusively in the progress stream that the user _needs_ to see, or might want to review after the script finishes. +Progress output is ephemeral, however, in that it doesn't stick around. You should not put anything exclusively in the progress stream that the user _needs_ to see, or might want to review after the script finishes. ## Use Write-Verbose to give details to someone running your script @@ -22,7 +22,7 @@ You should use verbose output for information that contains details about the va ## Use Write-Debug to give information to someone maintaining your script -You should use the debug output stream for output that is useful for script debugging ("Now entering main loop," "Result was null, skipping to end of loop"), or to display the value of a variable before a conditional statement, so the maintainer can break into the debugger if necessary. +You should use the debug output stream for output that is useful for script debugging (ie: "Now entering main loop" or "Result was null, skipping to end of loop"), or to display the value of a variable before a conditional statement, so the maintainer can break into the debugger if necessary. > TIP: When debugging you should be aware that you can set `$DebugPreference = "Continue"` to see this information on screen without entering a breakpoint prompt. @@ -30,9 +30,9 @@ You should use the debug output stream for output that is useful for script debu As we've already written elsewhere, you should probably [always use CmdletBinding](../Style-Guide/Code-Layout-and-Formatting.md#always-start-with-cmdletbinding). -However, it's particularly important when you're using Write-Verbose and Write-Debug, as the Verbose and Debug output streams are off by default, and the `[CmdletBinding()]` attribute enables the common `-Verbose` and `-Debug` switches which turn those streams on. +Using CmdletBinding is particularly important, however, when you're using Write-Verbose and Write-Debug, as the Verbose and Debug output streams are off by default, and the `[CmdletBinding()]` attribute enables the common `-Verbose` and `-Debug` switches which turn those streams on. -It also enables the switches for the Warning and Error streams, as well as ways of collecting those streams into variables. You should read the [always use CmdletBinding](../Style-Guide/Code-Layout-and-Formatting.md#always-start-with-cmdletbinding) topic for more information. +CmdletBinding also enables the switches for the Warning and Error streams, as well as ways of collecting those streams into variables. You should read the [always use CmdletBinding](../Style-Guide/Code-Layout-and-Formatting.md#always-start-with-cmdletbinding) topic for more information. ## Use Format Files for your custom objects @@ -44,11 +44,11 @@ You should avoid mixing different types of objects in the output of a single com For the sake of tools and command-search, you should indicate with the `[OutputType()]` attribute the output type(s) of your scripts, functions or cmdlets (see about_Functions_OutputTypeAttribute for more information). -When you do combine the output of multiple types objects, they should generally be derived from a common basetype (like FileInfo and DirectoryInfo come from System.IO.FileSystemInfo), or should have format or type files which cause them to output the same columns. In particular, you must avoid outputting strings interspersed in your output. +When you combine the output of multiple types objects, they should generally be derived from a common basetype (like FileInfo and DirectoryInfo come from System.IO.FileSystemInfo), or should have format or type files which cause them to output the same columns. In particular, you must avoid outputting strings interspersed in your output. ### Two important exceptions to the single-type rule -**For internal functions.** it's ok to return multiple different types because they won't be "output" to the user/host, and can offer significant savings (e.g. one database call with three table joins, instead of three database calls with two or three joins each). You can then call these functions and assign the output to multiple variables, like so: +**For internal functions** it's ok to return multiple different types because they won't be "output" to the user/host, and can offer significant savings (e.g. one database call with three table joins, instead of three database calls with two or three joins each). You can then call these functions and assign the output to multiple variables, like so: ```PowerShell $user, $group, $org = Get-UserGroupOrg From 9c8ab5710b5da08900a4c7a664030d964c079d88 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 17 Oct 2017 18:26:33 -0400 Subject: [PATCH 031/113] Attempt to use theme-faq Just testing the effect on the gitbook output --- book.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/book.json b/book.json index 4a44eee..d8db1ea 100644 --- a/book.json +++ b/book.json @@ -1,5 +1,10 @@ { + "plugins": [ + "theme-faq", + "-fontsettings", + "-sharing" + ], "structure": { "summary": "TableOfContents.md" } -} \ No newline at end of file +} From 0598b5fbc36b20d32255695c0a0403e2d15ab6c0 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 17 Oct 2017 18:30:52 -0400 Subject: [PATCH 032/113] Revert to normal book form It appears the FAQ theme requires us to break every point into it's own article. --- book.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/book.json b/book.json index d8db1ea..5610cef 100644 --- a/book.json +++ b/book.json @@ -1,9 +1,4 @@ { - "plugins": [ - "theme-faq", - "-fontsettings", - "-sharing" - ], "structure": { "summary": "TableOfContents.md" } From 59c707fb5eafcf31111a78b65b253ec0c7826106 Mon Sep 17 00:00:00 2001 From: Chad McCaffery Date: Fri, 27 Oct 2017 10:11:37 -0700 Subject: [PATCH 033/113] Summarize instead of duplicate Summarize a duplicated sentence and corrected spelling of 'unnecessary'. --- Style-Guide/Code-Layout-and-Formatting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 784a7a4..51644b2 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -60,7 +60,7 @@ function Write-Host { ... ``` -PowerShell uses PascalCase for _all_ public identifiers: module names, function or cmdlet names, class and enum names, public fields or properties, global variables and constants, etc. In fact, since the _parameters_ to PowerShell commands are actually _properties_ of .Net classes, even parameters use PascalCase rather than camelCase. Function names should follow PowerShell's `Verb-Noun` naming conventions, using PascalCase within both Verb and Noun. +As stated previously, PowerShell uses PascalCase for _all_ public identifiers. Function names should follow PowerShell's `Verb-Noun` naming conventions, using PascalCase within both Verb and Noun. A special case is made for two-letter acronyms in which both letters are capitalized, as in the variable `$PSBoundParameters` or the command `Get-PSDrive`. Note that ([as specified in the .NET guidelines](https://msdn.microsoft.com/en-us/library/ms229043#Anchor_1)) this does not affect the commonly capitalized (but not acronym) words "OK" and "ID" . You should also not extend it to compound acronyms, such as when Azure's Resource Manager (RM) meets a Virtual Machine (VM) in `Start-AzureRmVM`... @@ -181,7 +181,7 @@ Nested expressions `$( ... )` and variable delimiters `${...}` inside strings do #### Avoid using semicolons (`;`) at the end of each line. -PowerShell will not complain about extra semicolons, but they are unecessary, and get in the way when code is being edited or copy-pasted. They also result in extra do-nothing edits in source control when someone finally decides to delete them. +PowerShell will not complain about extra semicolons, but they are unnecessary, and get in the way when code is being edited or copy-pasted. They also result in extra do-nothing edits in source control when someone finally decides to delete them. They are also unecessary when declaring hashtables if you are already putting each element on it's own line: From 9c876ee52ef1bec7d01b75a9d97a2989d5aa2cd6 Mon Sep 17 00:00:00 2001 From: nveron Date: Mon, 6 Nov 2017 15:41:01 +0100 Subject: [PATCH 034/113] Code consistency on curly brace --- Style-Guide/Function-Structure.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Style-Guide/Function-Structure.md b/Style-Guide/Function-Structure.md index ba9ee17..128a9fe 100644 --- a/Style-Guide/Function-Structure.md +++ b/Style-Guide/Function-Structure.md @@ -40,8 +40,7 @@ function Get-USCitizenCapability { DrinkAlcohol = $false Vote = $false } - if ($Age -ge 18) - { + if ($Age -ge 18) { $Capabilities['MilitaryService'] = $true $Capabilities['Vote'] = $true } From 9f578f883796dafa505f179dcc6fc93730e2a64c Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Tue, 7 Nov 2017 22:02:50 -0500 Subject: [PATCH 035/113] Remove double-negative --- Style-Guide/Documentation-and-Comments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index 9b6405c..e7b7843 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -95,7 +95,7 @@ Param( ) ``` -It is also possible to write `.PARAMETER` statements with the rest of the documentation comments, but they will be less likely to be left un-updated if you put them closer to the actual code they document. +It is also possible to write `.PARAMETER` statements with the rest of the documentation comments, but experience shows they are more likely to be kept up-to-date if you put them closer to the code they document. ##### Provide Usage Examples From b2665bb32fabebe7989920ecf928286ed13a279d Mon Sep 17 00:00:00 2001 From: Anders Wahlqvist Date: Mon, 26 Mar 2018 15:28:51 +0200 Subject: [PATCH 036/113] Added comment about unary operators and whitespace --- Style-Guide/Code-Layout-and-Formatting.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 51644b2..50c878a 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -156,7 +156,7 @@ Lines should not have trailing whitespace. Extra spaces result in future edits w You should use a single space around parameter names and operators, including comparison operators and math and assignment operators, even when the spaces are not necessary for PowerShell to correctly parse the code. -One notable exception is when using colons to pass values to switch parameters: +A notable exception is when using colons to pass values to switch parameters: ```PowerShell # Do not write: @@ -166,6 +166,25 @@ $variable=Get-Content $FilePath -Wai:($ReadCount-gt0) -First($ReadCount*5) $variable = Get-Content -Path $FilePath -Wait:($ReadCount -gt 0) -TotalCount ($ReadCount * 5) ``` +Another exception is when using [Unary Operators](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators#unary-operators): + +```PowerShell +# Do not write: +$yesterdaysDate = (Get-Date).AddDays( - 1) + +$i = 0 +$i ++ + +# Instead write: +$yesterdaysDate = (Get-Date).AddDays(-1) + +$i = 0 +$i++ + +# Same principle should be applied when using a variable +$yesterdaysDate = (Get-Date).AddDays(-$i) +``` + #### Spaces around special characters White-space is (mostly) irrelevant to PowerShell, but its proper use is the key to writing easily readable code. From 814a9a2828c513336caa71bfe08462618d1a7f8b Mon Sep 17 00:00:00 2001 From: 1DontEx1st Date: Wed, 5 Sep 2018 10:28:03 -0400 Subject: [PATCH 037/113] Update Code-Layout-and-Formatting.md removed a stray "a" --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 51644b2..662cf22 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -8,7 +8,7 @@ Rules about indentation, line length, and capitalization are about consistency a We don't expect everyone to follow these guidelines, and rules for individual projects always trump these. Whether for legacy reasons, or to match guidelines for multiple languages in a single project, different projects may have different style guidelines. Since the goal is consistency, you should always abide by any style rules that are in place on the project you are contributing to. -If you do have a legacy project that is in source control and you decide to reformat code to adopt these rules, try to make all of your whitespace changes in a single a commit that does _nothing_ but edit the whitespace. You should never reformat the whitespace on a file as _part_ of a content change because it makes the changes hard to spot. +If you do have a legacy project that is in source control and you decide to reformat code to adopt these rules, try to make all of your whitespace changes in a single commit that does _nothing_ but edit the whitespace. You should never reformat the whitespace on a file as _part_ of a content change because it makes the changes hard to spot. #### Capitalization Conventions From 819e62663324fec281ada37a1019a2060e7efc89 Mon Sep 17 00:00:00 2001 From: Thomas Rayner Date: Mon, 1 Oct 2018 13:06:36 -0700 Subject: [PATCH 038/113] Update Naming-Conventions.md Changed references to Get-WmiObject to Get-Process since the WMI cmdlets are on the road to deprecation. --- Style-Guide/Naming-Conventions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index c634236..a0fc205 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -8,10 +8,10 @@ Every PowerShell scripter learns the actual command names, but different people ```PowerShell # Do not write: -gwmi -Class win32_service +gps -Name Explorer # Instead write: -Get-WmiObject -Class Win32_Service +Get-Process -Name Explorer ``` #### Use full parameter names. @@ -20,10 +20,10 @@ Because there are so many commands in PowerShell, it's impossible for every scri ```PowerShell # Do not write: -Get-WmiObject win32_service name,state +Get-Process Explorer # Instead write: -Get-WmiObject -Class win32_service -Property name,state +Get-Process -Name Explorer ``` #### Use full, explicit paths when possible. From 58dffb70dcd712a5f378f1fa146a03f18a106103 Mon Sep 17 00:00:00 2001 From: Stephen Owen Date: Tue, 2 Oct 2018 15:27:38 -0400 Subject: [PATCH 039/113] Added examples of Opening / closing braces Seemed like the life of this discussion issue has concluded, https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/24, so lets include the agreed upon examples inside the docs :) --- Style-Guide/Code-Layout-and-Formatting.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 51644b2..d3e5fbd 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -82,12 +82,21 @@ end{} You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and so on, but you should avoid writing scripts or functions without CmdletBinding, and you should always at least _consider_ making it take pipeline input. #### Open braces on the same line -Code folding is nicer in many editors. -(TODO: This is in discussion in [#24](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/24)) - +Code folding is nicer in many editors when a scriptblock is placed on the end of the same line, as in this example. + +```` +function Get-Noun { + end { + if($Wide) { + Get-Command | Sort-Object Noun -Unique | Format-Wide Noun + } else { + Get-Command | Sort-Object Noun -Unique | Select-Object -Expand Noun + } + } +} +```` #### Closing braces always on their own line -Because that's how they're supposed to be! -(TODO: This is in discussion in [#24](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/24)) +Note the above example again, community guidelines recommend placing your closing braces on their own line. This practice makes it easier to pair up matching opening and closing braces when looking to see where a particular scriptblock ends. #### Prefer: param() begin, process, end That's the order PowerShell will execute it in From 329e3cd3ba74319f11ee7ae131233f77e48681d2 Mon Sep 17 00:00:00 2001 From: Stephen Owen Date: Wed, 3 Oct 2018 10:44:58 -0400 Subject: [PATCH 040/113] pr feedback --- Style-Guide/Code-Layout-and-Formatting.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index d3e5fbd..8fbea1d 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -81,13 +81,15 @@ end{} You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and so on, but you should avoid writing scripts or functions without CmdletBinding, and you should always at least _consider_ making it take pipeline input. -#### Open braces on the same line +#### Follow the one-true-brace style. +Open braces always go on the same line + Code folding is nicer in many editors when a scriptblock is placed on the end of the same line, as in this example. ```` function Get-Noun { end { - if($Wide) { + if ($Wide) { Get-Command | Sort-Object Noun -Unique | Format-Wide Noun } else { Get-Command | Sort-Object Noun -Unique | Select-Object -Expand Noun From c0b62840712ffe176cd654cfc6056ce4dbc365d0 Mon Sep 17 00:00:00 2001 From: Stephen Owen Date: Wed, 3 Oct 2018 10:51:23 -0400 Subject: [PATCH 041/113] Implementing JayKul's feedback --- Style-Guide/Code-Layout-and-Formatting.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 8fbea1d..9ba54c2 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -81,8 +81,10 @@ end{} You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and so on, but you should avoid writing scripts or functions without CmdletBinding, and you should always at least _consider_ making it take pipeline input. -#### Follow the one-true-brace style. -Open braces always go on the same line +#### Brace yourself: Follow the one-true-brace style. +Open braces always go on the same line. + +This style really won in the PowerShell community partly because the style is one of two used in C languages --it's a variant of the K&R (Kernighan and Ritchie) style from their book The C Programming Language-- but also because for the first few years of PowerShell's existence, this was the only style that could be typed at the command line. Code folding is nicer in many editors when a scriptblock is placed on the end of the same line, as in this example. @@ -97,8 +99,10 @@ function Get-Noun { } } ```` -#### Closing braces always on their own line -Note the above example again, community guidelines recommend placing your closing braces on their own line. This practice makes it easier to pair up matching opening and closing braces when looking to see where a particular scriptblock ends. +#### Closing braces start a new line +Note the above example again, community guidelines recommend following the ['One-True-Brace-Style'](https://www.wikiwand.com/en/Indentation_style#/K&R_style) placing your closing braces on their own line. This practice makes it easier to pair up matching opening and closing braces when looking to see where a particular scriptblock ends, and allows one to insert new lines of code between any two lines. + +To reiterate, these are community best practices, and a lot of the code you'll find online from community leaders will follow these guidelines. That doesn't mean that those who follow different style guidelines are wrong. You may be the one to set the course for your company or your own project; we simply offer this guidance for your consideration. #### Prefer: param() begin, process, end That's the order PowerShell will execute it in From d4420eb74904c974710921623d87da4894ac1db7 Mon Sep 17 00:00:00 2001 From: C-Bam <41115346+C-Bam@users.noreply.github.com> Date: Wed, 3 Oct 2018 18:18:53 +0300 Subject: [PATCH 042/113] Update Readability.md ForEach to foreach. I just saw that :) --- Style-Guide/Readability.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Readability.md b/Style-Guide/Readability.md index 87f1c44..4179485 100644 --- a/Style-Guide/Readability.md +++ b/Style-Guide/Readability.md @@ -26,7 +26,7 @@ Continuing in that vein, understand that the following are basically guidelines First, format your code properly. The convention is to indent within constructs, to make it clearer what "belongs to" the construct. ```PowerShell -ForEach ($computer in $computers) { +foreach ($computer in $computers) { Do-This Get-Those } From b9d6986291e6ee2ffb9be8e860dd4f9218365ec9 Mon Sep 17 00:00:00 2001 From: Aurimas Navardauskas Date: Wed, 10 Oct 2018 16:21:50 +0300 Subject: [PATCH 043/113] Update Function-Structure.md judging by the good example it should be pascal case? --- Style-Guide/Function-Structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Function-Structure.md b/Style-Guide/Function-Structure.md index 128a9fe..831b6f9 100644 --- a/Style-Guide/Function-Structure.md +++ b/Style-Guide/Function-Structure.md @@ -15,7 +15,7 @@ function MyFunction ($param1, $param2) { For Advanced Functions and scripts use the format of **** for naming. For a list of approved verbs the cmdlet `Get-Verb` will list them. On the noun side it can be composed of more than one joined word - using Camel Case and only singular nouns. + using Pascal Case and only singular nouns. In Advanced Functions do not use the keyword `return` to return an object. From 8c11368a6c67777eb23aaf8e62f0b7e9e353c160 Mon Sep 17 00:00:00 2001 From: Joel <32407840+vexx32@users.noreply.github.com> Date: Fri, 12 Oct 2018 16:29:56 -0400 Subject: [PATCH 044/113] Resolve merge conflicts and consolidate approved edits --- Style-Guide/Code-Layout-and-Formatting.md | 144 ++++++++++++---------- 1 file changed, 79 insertions(+), 65 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 2cbffd8..096aa88 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -2,13 +2,13 @@ These guidelines are about readability. Some of them are arbitrary rules, but they are based on decades of traditions in programming, so while you may disagree with some rules (and should always follow the rules of individual projects), when we ask you to leave an empty line after a closing function brace, or two lines before functions, we're not being capricious, we're doing so because it makes it easier for experienced developers to scan your code. -#### Maintain consistency in layout +#### Maintain Consistency in Layout Rules about indentation, line length, and capitalization are about consistency across code bases. Long practice has shown that it's easier to read and understand code when it looks familiar and you're not being distracted by details, which means that it's better for everyone in the community to follow a single set of rules. We don't expect everyone to follow these guidelines, and rules for individual projects always trump these. Whether for legacy reasons, or to match guidelines for multiple languages in a single project, different projects may have different style guidelines. Since the goal is consistency, you should always abide by any style rules that are in place on the project you are contributing to. -If you do have a legacy project that is in source control and you decide to reformat code to adopt these rules, try to make all of your whitespace changes in a single commit that does _nothing_ but edit the whitespace. You should never reformat the whitespace on a file as _part_ of a content change because it makes the changes hard to spot. +If you do have a legacy project that is in source control and you decide to reformat code to adopt these rules, try to make all of your whitespace changes in a single a commit that does _nothing_ but edit the whitespace. You should never reformat the whitespace on a file as _part_ of a content change because it makes the changes hard to spot. #### Capitalization Conventions @@ -25,7 +25,7 @@ PowerShell uses PascalCase for _all_ public identifiers: module names, function PowerShell language keywords are written in lower case (yes, even `foreach` and `dynamicparam`), as well as operators such as `-eq` and `-match`. The keywords in comment-based help are written in UPPERCASE to make it easy to spot them among the dense prose of documentation. -```posh +```powershell function Write-Host { <# .SYNOPSIS @@ -55,8 +55,7 @@ function Write-Host { [System.ConsoleColor] $BackgroundColor ) - begin - { + begin { ... ``` @@ -68,72 +67,79 @@ A special case is made for two-letter acronyms in which both letters are capital If you wish, you may use camelCase for variables within your functions (or modules) to distinguish _private_ variables from parameters, but this is a matter of taste. Shared variables should be distinguished by using their scope name, such as `$Script:PSBoundParameters` or `$Global:DebugPreference`. If you are using camelCase for a variable that starts with a two-letter acronym (where both letters are capitalized), both letters should be set to lowercase (such as `adComputer`). +#### Open braces on the same line -#### Always Start With CmdletBinding - -All of your scripts or functions should start life as something like this snippet: +This can be considered a matter of consistency; several common cmdlets in PowerShell take script blocks as _parameters_ (e.g., `ForEach-Object`), and in these cases it is functionally impossible to place the opening brace on a new line _without_ use of a line-continuator (i.e., ``` ` ```, a backtick), which should generally be avoided. +```powershell +$Data | ForEach-Object { + $_.Item -as [int] +} ``` -[CmdletBinding()]param() -process{} -end{} + +v.s. + +```powershell +foreach ($Entry in $Data) +{ + $Entry.Item -as [int] +} ``` -You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and so on, but you should avoid writing scripts or functions without CmdletBinding, and you should always at least _consider_ making it take pipeline input. +As such, both native keywords and function parameters should include opening braces on the _same_ line. -#### Brace yourself: Follow the one-true-brace style. -Open braces always go on the same line. +Code folding is also nicer in many editors. -This style really won in the PowerShell community partly because the style is one of two used in C languages --it's a variant of the K&R (Kernighan and Ritchie) style from their book The C Programming Language-- but also because for the first few years of PowerShell's existence, this was the only style that could be typed at the command line. +#### Closing Braces Always on Their Own Line -Code folding is nicer in many editors when a scriptblock is placed on the end of the same line, as in this example. +Once again, this makes code-folding much more sensible in many editors. -```` -function Get-Noun { - end { - if ($Wide) { - Get-Command | Sort-Object Noun -Unique | Format-Wide Noun - } else { - Get-Command | Sort-Object Noun -Unique | Select-Object -Expand Noun - } - } -} -```` -#### Closing braces start a new line -Note the above example again, community guidelines recommend following the ['One-True-Brace-Style'](https://www.wikiwand.com/en/Indentation_style#/K&R_style) placing your closing braces on their own line. This practice makes it easier to pair up matching opening and closing braces when looking to see where a particular scriptblock ends, and allows one to insert new lines of code between any two lines. +The exception to this rule may be in cases where the script block is a parameter, and further parameters must still be added. However, in the interests of improving code-folding, readability, and maintainability, placing such parameters _before_ the script block parameter should be considered, where possible. -To reiterate, these are community best practices, and a lot of the code you'll find online from community leaders will follow these guidelines. That doesn't mean that those who follow different style guidelines are wrong. You may be the one to set the course for your company or your own project; we simply offer this guidance for your consideration. +#### Always Start With CmdletBinding -#### Prefer: param() begin, process, end -That's the order PowerShell will execute it in -(TODO) +All of your scripts or functions should start life as something like this snippet: +```powershell +[CmdletBinding()] +param() +process {} +end {} +``` -#### Indentation +You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and necessary valiation and so on, but you should **avoid** writing scripts or functions without `[CmdletBinding()]`, and you should always at least _consider_ making it take pipeline input. -##### Use four *spaces* per indentation level. +#### Prefer: param(), begin, process, end -This is what PowerShell ISE does and understands, and it's the default for most code editors. As always, existing projects may have different standards, but for public code, please stick to 4 spaces, and the rest of us will try to do the same. +Having a script written in the order of execution makes its intent more clear. There is no functional purpose to having `begin` be declared _after_ `process`. Although it _will_ still be executed in the correct order, writing in such a fashion significantly detracts from the readability of a script. -The 4-space rule is optional for continuation lines. Hanging indents (when indenting a wrapped command which was too long) may be indented more than one indentation level, or may even be indented an odd number of spaces to line up with a method call or parameter block. +As a general rule, unreadable scripts are also difficult to maintain or debug. -```PowerShell +#### Indentation -# This is ok -$MyObj.GetData( - $Param1, - $Param2, - $Param3, - $Param4 - ) +##### Use four *spaces* per indentation level -# This is better -$MyObj.GetData($Param1, - $Param2, - $Param3, - $Param4) +Usually you use the `[Tab]` key to indent, but most editors can be configured to insert spaces instead of actual tab characters when you indent. For most programming languages and editors (including PowerShell ISE) the default is four spaces, and that's what we recommend. Different teams and projects may have different standards, and you should abide by them in the interest of maintaining consistency of style in a given project. + +```powershell +function Test-Code { + foreach ($exponent in 1..10) { + [Math]::Pow(2, $exponent) + } +} ``` +Indenting more than 4-spaces is acceptable for continuation lines (when you're wrapping a line which was too long). In such cases you might indent more than one level, or even indent indent an odd number of spaces to line up with a method call or parameter block on the line before. + +```powershell +function Test-Code { + foreach ($base in 1,2,4,8,16) { + foreach ($exponent in 1..10) { + [System.Math]::Pow($base, + $exponent) + } +} +``` #### Maximum Line Length @@ -141,19 +147,21 @@ Limit lines to 115 characters when possible. The PowerShell console is, by default, 120 characters wide, but it allows only 119 characters on output lines, and when entering multi-line text, PowerShell uses a line continuation prompt: `>>> ` and thus limits your line length to 116 anyway. +Additionally, keeping lines to a set width allows scripts to be read in _one_ direction (top to bottom) with no horizontal scrolling required. For many, having to scroll in both directions detracts from a smooth reading and comprehension of the script. + Most of us work on widescreen monitors these days, and there is little reason to keep a narrow line width, however, keeping files relatively narrow allows for side-by-side editing, so even narrower guidelines may be established by a given project. Be sure to check when you're working on someone else's project. -The preferred way to avoid long lines is to use splatting (see [About Splatting](https://technet.microsoft.com/en-us/library/jj672955.aspx)) and PowerShell's implied line continuation inside parentheses, brackets, and braces -- these should always be used in preference to the backtick for line continuation when applicable, even for strings: +The preferred way to avoid long lines is to use splatting (see [Get-Help about_Splatting](https://technet.microsoft.com/en-us/library/jj672955.aspx)) and PowerShell's implied line continuation inside parentheses, brackets, and braces -- these should **always** be used in preference to the backtick for line continuation when applicable, even for strings: -``` +```powershell Write-Host ("This is an incredibly important, and extremely long message. " + "We cannot afford to leave any part of it out, nor do we want line-breaks in the output. " + - "Using string concatenation let's us use short lines here, and still get a long line in the output") + "Using string concatenation lets us use short lines here, and still get a long line in the output") ``` -#### Blank lines +#### Blank Lines and Whitespace -Surround function and class definitions with two blank lines. +Surround function and class definitions with _two_ blank lines. Method definitions within a class are surrounded by a single blank line. @@ -171,7 +179,7 @@ Lines should not have trailing whitespace. Extra spaces result in future edits w You should use a single space around parameter names and operators, including comparison operators and math and assignment operators, even when the spaces are not necessary for PowerShell to correctly parse the code. -A notable exception is when using colons to pass values to switch parameters: +One notable exception is when using colons to pass values to switch parameters: ```PowerShell # Do not write: @@ -202,28 +210,34 @@ $yesterdaysDate = (Get-Date).AddDays(-$i) #### Spaces around special characters -White-space is (mostly) irrelevant to PowerShell, but its proper use is the key to writing easily readable code. +White-space is (mostly) irrelevant to PowerShell, but its proper use is key to writing easily readable code. Use a single space after commas and semicolons, and around pairs of curly braces. -Avoid extra spaces inside parenthesis or square braces. +Avoid unnecessary extra spaces inside parenthesis or square braces. + +Subexpressions `$( ... )` and script blocks `{ ... }` should have a single space _inside_ the enclosing braces or parentheses to make code stand out and be more readable. -Nested expressions `$( ... )` and script blocks `{ ... }` should have a single space _inside_ them to make code stand out and be more readable. +Subexpressions `$( ... )` and variable delimiters `${...}` nested inside strings should not include additional space _surrounding_ them, unless it is desired for the final string to include them. -Nested expressions `$( ... )` and variable delimiters `${...}` inside strings do not need spaces _outside_, since that would become a part of the string. +```powershell +$Var = 1 +"This is a string with one (${Var}) delimited variable." +"This is $( 2 - 1 ) string with $( 1 + 1 ) numbers contained within." +``` -#### Avoid using semicolons (`;`) at the end of each line. +#### Avoid Using Semicolons (`;`) as Line Terminators -PowerShell will not complain about extra semicolons, but they are unnecessary, and get in the way when code is being edited or copy-pasted. They also result in extra do-nothing edits in source control when someone finally decides to delete them. +PowerShell will not complain about extra semicolons, but they are unnecessary, and can get in the way when code is being edited or copy-pasted. They also result in extra do-nothing edits in source control when someone finally decides to delete them. They are also unecessary when declaring hashtables if you are already putting each element on it's own line: ```PowerShell -# This is the preferred way to declare a hashtable if it must go past one line: +# This is the preferred way to declare a hashtable if it extends past one line: $Options = @{ - Margin = 2 - Padding = 2 + Margin = 2 + Padding = 2 FontSize = 24 } ``` From b9c5bd5bdea75ca3edfa80e14eaedeaf91db62cf Mon Sep 17 00:00:00 2001 From: David Zampino Date: Wed, 24 Oct 2018 15:41:52 -0500 Subject: [PATCH 045/113] Add whitespace to equal signs in parameters --- Best-Practices/Error-Handling.md | 4 +- Best-Practices/TODO.md | 6 +-- Style-Guide/Code-Layout-and-Formatting.md | 2 +- Style-Guide/Documentation-and-Comments.md | 8 ++-- Style-Guide/Function-Structure.md | 46 +++++++++++------------ 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/Best-Practices/Error-Handling.md b/Best-Practices/Error-Handling.md index 3461775..46c627f 100644 --- a/Best-Practices/Error-Handling.md +++ b/Best-Practices/Error-Handling.md @@ -2,9 +2,9 @@ When trapping an error, try to use -ErrorAction Stop on cmdlets to generate terminating, trappable exceptions. -# ERR-02 Use $ErrorActionPreference='Stop' or 'Continue' when calling non-cmdlets +# ERR-02 Use $ErrorActionPreference = 'Stop' or 'Continue' when calling non-cmdlets -When executing something other than a cmdlet, set $ErrorActionPreference='Stop' before executing, and re-set to Continue afterwards. If you're concerned about using -ErrorAction because it will bail on the entire pipeline, then you've probably over-constructed the pipeline. Consider using a more scripting-construct-style approach, because those approaches are inherently better for automated error handling. +When executing something other than a cmdlet, set $ErrorActionPreference = 'Stop' before executing, and re-set to Continue afterwards. If you're concerned about using -ErrorAction because it will bail on the entire pipeline, then you've probably over-constructed the pipeline. Consider using a more scripting-construct-style approach, because those approaches are inherently better for automated error handling. Ideally, whatever command or code you think might bomb should be dealing with one thing: querying one computer, deleting one file, updating one user. That way, if an error occurs, you can handle it and then get on with the next thing. diff --git a/Best-Practices/TODO.md b/Best-Practices/TODO.md index fa42d17..8e7edb8 100644 --- a/Best-Practices/TODO.md +++ b/Best-Practices/TODO.md @@ -149,8 +149,8 @@ It doesn't do anything, and it confuses future readers. When prompted for a mandatory parameter, a user can request HelpText, but can't look at the documentation. It's frequently useful to duplicate at least the first sentence or two of the parameter help. ``` -[Parameter(Position=1, Mandatory=$true, ValueFromPipeline=$true, -ValueFromPipelineByPropertyName=$true, HelpText='The name of the file to read')] +[Parameter(Position = 1, Mandatory = $true, ValueFromPipeline = $true, +ValueFromPipelineByPropertyName = $true, HelpText = 'The name of the file to read')] [Alias('PSPath','FullName','Path')] [String]$File ``` @@ -208,7 +208,7 @@ Discuss: when is this critical (-whatif) and optional (-confirm_ Discuss: when should you call PSCmdlet.ShouldProcess vs PSCmdlet.ShouldContinue (-Force) ``` -[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")] +[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium")] param([Switch]$Force) $RejectAll = $false; diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 2cbffd8..8548e18 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -39,7 +39,7 @@ function Write-Host { #> [CmdletBinding()] param( - [Parameter(Position=0, ValueFromPipeline=$true, ValueFromRemainingArguments=$true)] + [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromRemainingArguments = $true)] [PSObject] $Object, diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index e7b7843..5d24d5b 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -84,9 +84,9 @@ Examples can be found in the ISE snippets: ```powershell Param( # Param1 help description - [Parameter(Mandatory=$true, - ValueFromPipelineByPropertyName=$true, - Position=0)] + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 0)] $Param1,     # Param2 help description @@ -116,7 +116,7 @@ function Test-Help { param( # This parameter doesn't do anything. # Aliases: MP - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [Alias("MP")] [String]$MandatoryParameter ) diff --git a/Style-Guide/Function-Structure.md b/Style-Guide/Function-Structure.md index 831b6f9..dcc3fd9 100644 --- a/Style-Guide/Function-Structure.md +++ b/Style-Guide/Function-Structure.md @@ -28,9 +28,9 @@ function Get-USCitizenCapability { [CmdletBinding()] [OutputType([psobject])] param ( - [Parameter(Mandatory=$true, - ValueFromPipelineByPropertyName=$true, - Position=0)] + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 0)] [int16] $Age ) @@ -55,9 +55,9 @@ function Get-USCitizenCapability { [CmdletBinding()] [OutputType([psobject])] param ( - [Parameter(Mandatory=$true, - ValueFromPipelineByPropertyName=$true, - Position=0)] + [Parameter(Mandatory = $true, + ValueFromPipelineByPropertyName = $true, + Position = 0)] [int16] $Age ) @@ -87,23 +87,23 @@ function Get-USCitizenCapability { If the function returns different object types depending on the parameter set provide one per parameter set. ```PowerShell -[OutputType([], ParameterSetName="")] -[OutputType("", ParameterSetName="")] +[OutputType([], ParameterSetName = "")] +[OutputType("", ParameterSetName = "")] ``` #### When a ParameterSetName is used in any of the parameters, always provide a DefaultParameterSetName in the CmdletBinding attribute. ```PowerShell function Get-User { - [CmdletBinding(DefaultParameterSetName="ID")] - [OutputType("System.Int32", ParameterSetName="ID")] - [OutputType([String], ParameterSetName="Name")] + [CmdletBinding(DefaultParameterSetName = "ID")] + [OutputType("System.Int32", ParameterSetName = "ID")] + [OutputType([String], ParameterSetName = "Name")] param ( - [parameter(Mandatory=$true, ParameterSetName="ID")] + [parameter(Mandatory = $true, ParameterSetName = "ID")] [Int[]] $UserID, - [parameter(Mandatory=$true, ParameterSetName="Name")] + [parameter(Mandatory = $true, ParameterSetName = "Name")] [String[]] $UserName ) @@ -119,7 +119,7 @@ function Get-User { ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [AllowNull()] [String] $ComputerName @@ -132,7 +132,7 @@ function Get-User { ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [AllowEmptyString()] [String] $ComputerName @@ -145,7 +145,7 @@ function Get-User { ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [String[]] $ComputerName @@ -161,7 +161,7 @@ function Get-User { ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateCount(1,5)] [String[]] $ComputerName @@ -177,7 +177,7 @@ function Get-User { ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateLength(1,10)] [String[]] $ComputerName @@ -192,7 +192,7 @@ function Get-User { pattern. ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidatePattern("[0-9][0-9][0-9][0-9]")] [String[]] $ComputerName @@ -206,7 +206,7 @@ function Get-User { if any value is outside that range. ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateRange(0,10)] [Int] $Attempts @@ -243,7 +243,7 @@ function Get-User { ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateSet("Low", "Average", "High")] [String[]] $Detail @@ -264,7 +264,7 @@ function Get-User { match the specified type.) ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] $ID ) @@ -279,7 +279,7 @@ function Get-User { array. ```PowerShell param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [String[]] $UserName From e7f675c6b35961973de96ec758120772d62354b7 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Mon, 12 Nov 2018 16:47:20 -0500 Subject: [PATCH 046/113] Update wording in code-layout and formatting --- Style-Guide/Code-Layout-and-Formatting.md | 80 +++++++++++++---------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 096aa88..52469b4 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -8,7 +8,7 @@ Rules about indentation, line length, and capitalization are about consistency a We don't expect everyone to follow these guidelines, and rules for individual projects always trump these. Whether for legacy reasons, or to match guidelines for multiple languages in a single project, different projects may have different style guidelines. Since the goal is consistency, you should always abide by any style rules that are in place on the project you are contributing to. -If you do have a legacy project that is in source control and you decide to reformat code to adopt these rules, try to make all of your whitespace changes in a single a commit that does _nothing_ but edit the whitespace. You should never reformat the whitespace on a file as _part_ of a content change because it makes the changes hard to spot. +If you do have a legacy project that is in source control and you decide to reformat code to adopt these rules, try to make all of your whitespace changes in a single commit that does _nothing_ but edit the whitespace. You should never reformat the whitespace on a file as _part_ of a content change because it makes the changes hard to spot. #### Capitalization Conventions @@ -67,34 +67,38 @@ A special case is made for two-letter acronyms in which both letters are capital If you wish, you may use camelCase for variables within your functions (or modules) to distinguish _private_ variables from parameters, but this is a matter of taste. Shared variables should be distinguished by using their scope name, such as `$Script:PSBoundParameters` or `$Global:DebugPreference`. If you are using camelCase for a variable that starts with a two-letter acronym (where both letters are capitalized), both letters should be set to lowercase (such as `adComputer`). -#### Open braces on the same line +#### One True Brace Style -This can be considered a matter of consistency; several common cmdlets in PowerShell take script blocks as _parameters_ (e.g., `ForEach-Object`), and in these cases it is functionally impossible to place the opening brace on a new line _without_ use of a line-continuator (i.e., ``` ` ```, a backtick), which should generally be avoided. +This guide recommends the so-called ["One True Brace Style" variant to K&R](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81#issuecomment-285835313), which requires that every braceable _statement_ should have the opening brace on the _end of a line_, and the closing brace at the _beginning of a line_. -```powershell -$Data | ForEach-Object { - $_.Item -as [int] -} -``` - -v.s. +There is one notable exception when passing small scriptblocks to parameters (where K&R would allow leaving off the braces entirely), we allow putting the entire statement on a single line. ```powershell -foreach ($Entry in $Data) -{ - $Entry.Item -as [int] +enum Color { + Black, + White } -``` -As such, both native keywords and function parameters should include opening braces on the _same_ line. - -Code folding is also nicer in many editors. - -#### Closing Braces Always on Their Own Line +function Test-Code { + [CmdletBinding()] + param( + [int]$ParameterOne + ) + end { + if (10 -gt $ParameterOne) { + "Greater" + } else { + "Lesser" + } + } +} -Once again, this makes code-folding much more sensible in many editors. +# An Exception case: +Get-ChildItem | Where-Object { $_.Length -gt 10mb } +``` +The primary reason for this recommendation is practical: there are no exceptions necessary when following this rule, and when code is written following this style, _new lines_ of code can be inserted between any two lines with no risk of accidentally breaking the code by separating braces from their statement blocks. Thus, it's easier to follow, and makes errors less likely. -The exception to this rule may be in cases where the script block is a parameter, and further parameters must still be added. However, in the interests of improving code-folding, readability, and maintainability, placing such parameters _before_ the script block parameter should be considered, where possible. +Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some addition reasonning here: First: in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second: PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. #### Always Start With CmdletBinding @@ -103,23 +107,25 @@ All of your scripts or functions should start life as something like this snippe ```powershell [CmdletBinding()] param() -process {} -end {} +process { +} +end { +} ``` You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and necessary valiation and so on, but you should **avoid** writing scripts or functions without `[CmdletBinding()]`, and you should always at least _consider_ making it take pipeline input. #### Prefer: param(), begin, process, end -Having a script written in the order of execution makes its intent more clear. There is no functional purpose to having `begin` be declared _after_ `process`. Although it _will_ still be executed in the correct order, writing in such a fashion significantly detracts from the readability of a script. +Having a script written in the order of execution makes the intent clearer. Since there is no functional reason to have these blocks out of order (they _will_ still be executed in the normal order), writing them out of order can be confusing, and makes code more difficult to maintain and debug. -As a general rule, unreadable scripts are also difficult to maintain or debug. +More explicit code is more maintainable. While PowerShell allows leaving off the explicit name of the `end` block (and even has a `filter` keyword that converts the anonymous block to a `process` block), we recommend against using these features as it results in less explicit code. #### Indentation ##### Use four *spaces* per indentation level -Usually you use the `[Tab]` key to indent, but most editors can be configured to insert spaces instead of actual tab characters when you indent. For most programming languages and editors (including PowerShell ISE) the default is four spaces, and that's what we recommend. Different teams and projects may have different standards, and you should abide by them in the interest of maintaining consistency of style in a given project. +Usually you will press the `[Tab]` key to indent, but most editors can be configured to insert spaces instead of actual tab characters. For most programming languages and editors (including PowerShell ISE) the default is four spaces, and that's what we recommend. Different teams and projects may have different standards, and when contributing to a project, you should abide by the predominant style, of course. ```powershell function Test-Code { @@ -145,11 +151,15 @@ function Test-Code { Limit lines to 115 characters when possible. -The PowerShell console is, by default, 120 characters wide, but it allows only 119 characters on output lines, and when entering multi-line text, PowerShell uses a line continuation prompt: `>>> ` and thus limits your line length to 116 anyway. +Keeping lines to a small width allows scripts to be read in _one_ direction (top to bottom) without scrolling back-and-forth horizontally. What, exactly, this width should be is a one of the favorite arguing points among developers on the internet (more splintered than emacs vs vi or gnu GPL vs MIT). + +In this guide we use two particular sources for the maximum line width: + +The PowerShell console is, by default, 120 characters wide, but it allows only 119 characters on output lines, and when entering multi-line text, PowerShell uses a line continuation prompt: `>>> ` and thus limits your line length to 116 anyway. -Additionally, keeping lines to a set width allows scripts to be read in _one_ direction (top to bottom) with no horizontal scrolling required. For many, having to scroll in both directions detracts from a smooth reading and comprehension of the script. +Github's current maximum line width varies between 121 and 126 depending on your browser and OS (and thus, font). However, the 115 line length suggested by PowerShell would be enough to even allow side-by-side diffs to be displayed without scrolling or wrapping on the current "standard" 1080p monitor. -Most of us work on widescreen monitors these days, and there is little reason to keep a narrow line width, however, keeping files relatively narrow allows for side-by-side editing, so even narrower guidelines may be established by a given project. Be sure to check when you're working on someone else's project. +Again, this is a particularly flexible rule, and you should always follow the guidelines of projects when you're contributing to other people's pojects. Although most of us work on widescreen monitors, not everyone can see well without magnification or extremely large fonts. The preferred way to avoid long lines is to use splatting (see [Get-Help about_Splatting](https://technet.microsoft.com/en-us/library/jj672955.aspx)) and PowerShell's implied line continuation inside parentheses, brackets, and braces -- these should **always** be used in preference to the backtick for line continuation when applicable, even for strings: @@ -210,23 +220,23 @@ $yesterdaysDate = (Get-Date).AddDays(-$i) #### Spaces around special characters -White-space is (mostly) irrelevant to PowerShell, but its proper use is key to writing easily readable code. +White-space is (mostly) irrelevant to PowerShell, but its proper use is key to writing easily readable code. Use a single space after commas and semicolons, and around pairs of curly braces. -Avoid unnecessary extra spaces inside parenthesis or square braces. +Subexpressions `$( ... )` and scriptblocks `{ ... }` should have a single space on the _inside_ of the braces or parentheses to improve readability by making code blocks stand out -- and to further distinguish scriptblocks from variable delimiter braces `${...}` -Subexpressions `$( ... )` and script blocks `{ ... }` should have a single space _inside_ the enclosing braces or parentheses to make code stand out and be more readable. - -Subexpressions `$( ... )` and variable delimiters `${...}` nested inside strings should not include additional space _surrounding_ them, unless it is desired for the final string to include them. +Avoid unnecessary spaces inside parenthesis or square braces. ```powershell $Var = 1 "This is a string with one (${Var}) delimited variable." -"This is $( 2 - 1 ) string with $( 1 + 1 ) numbers contained within." +"There are $( (Get-ChildItem).Count ) files." ``` +Obviously, these rules should not be applied in such a way as to affect output. + #### Avoid Using Semicolons (`;`) as Line Terminators PowerShell will not complain about extra semicolons, but they are unnecessary, and can get in the way when code is being edited or copy-pasted. They also result in extra do-nothing edits in source control when someone finally decides to delete them. From 242bc0d77a55842b43c6507b462a72eafc9301d8 Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Sun, 24 Feb 2019 15:24:11 +0100 Subject: [PATCH 047/113] Fix several typos --- Best-Practices/Performance.md | 3 +-- Best-Practices/TODO.md | 4 ++-- LICENSE.md | 4 ++-- README.md | 4 ++-- Style-Guide/Code-Layout-and-Formatting.md | 16 ++++++++-------- Style-Guide/Documentation-and-Comments.md | 2 +- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Best-Practices/Performance.md b/Best-Practices/Performance.md index 98bac45..8c93574 100644 --- a/Best-Practices/Performance.md +++ b/Best-Practices/Performance.md @@ -65,5 +65,4 @@ This example reverts back to a native PowerShell approach, using commands and pa You will generally find that it is possible to conform with the community's general aesthetic preferences while still maintaining a good level of performance. Doing so may require more work - such as writing PowerShell wrapper commands around underlying .NET Framework classes. Most would argue that, for a tool that is intended for long-term use, the additional work is a worthwhile investment. -The moral here is that both aesthetic and performance are important considerations, and without some work context, neither is inherently more important than the other. It is often possible, with the right technique, to satisfy both. As a general practice, you should avoid giving up on aesthetics solely because of performance concesrns - when possible, make the effort to satisfy both performance and aesthetics. - +The moral here is that both aesthetic and performance are important considerations, and without some work context, neither is inherently more important than the other. It is often possible, with the right technique, to satisfy both. As a general practice, you should avoid giving up on aesthetics solely because of performance concerns - when possible, make the effort to satisfy both performance and aesthetics. diff --git a/Best-Practices/TODO.md b/Best-Practices/TODO.md index fa42d17..5442e0a 100644 --- a/Best-Practices/TODO.md +++ b/Best-Practices/TODO.md @@ -246,7 +246,7 @@ My choice: Configuration module. Otherwise, use clixml (or XAML) to persist to A #### Provide aliases in your modules You should feel free to create and use aliases within your modules. In some cases, you can even improve readability by using an alias without the verb, or shortening command names. -For exported aliases, follow the guidance of Microsoft ("ip" for import, "s" for set, "g" for get, "r" for remove, etc.), make up somethign for your nouns. +For exported aliases, follow the guidance of Microsoft ("ip" for import, "s" for set, "g" for get, "r" for remove, etc.), make up something for your nouns. Use `New-Alias ... -ErrorAction SilentlyContinue` to avoid overwriting existing aliases. @@ -293,7 +293,7 @@ Additionally, avoid using `[string]` with ParameterSets because anything can be When passing on parameters to another command, you should be _at least_ as strongly typed as the other command, to avoid casting exceptions within your script. -One notable exception is when you could accept more than one type. In PowerShell you can speficy parameter set overloads, but you can't change the type of a parameter. +One notable exception is when you could accept more than one type. In PowerShell you can specify parameter set overloads, but you can't change the type of a parameter. ### Don't reinvent the wheel diff --git a/LICENSE.md b/LICENSE.md index 31ba7a1..6c3ab47 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -8,7 +8,7 @@ This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 Inter **Adapt** — remix, transform, and build upon the material -The authors encourage you to redistribute this content as widely as possible, but require that you give credit to the primary authors below, and that you notify us on github of any improvements you make. +The authors encourage you to redistribute this content as widely as possible, but require that you give credit to the primary authors below, and that you notify us on GitHub of any improvements you make. #### Credits @@ -29,6 +29,6 @@ Portions copyright (c) Joel Bennett, 2015 The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting [https://github.com/PoshCode/PowerShellPracticeAndStyle](https://github.com/PoshCode/PowerShellPracticeAndStyle) -The *PowerShell Style Guide* in particular is in PREVIEW, and we are still actively working out our disagreements about the rules in the guide through the github issues system. Please don't be suprised if over then next few weeks we change rules to contradict what they say at this current moment. +The *PowerShell Style Guide* in particular is in PREVIEW, and we are still actively working out our disagreements about the rules in the guide through the GitHub issues system. Please don't be surprised if over then next few weeks we change rules to contradict what they say at this current moment.

Creative Commons License

diff --git a/README.md b/README.md index bffd698..56bf9fd 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 Inter **Adapt** — remix, transform, and build upon the material -The authors encourage you to redistribute this content as widely as possible, but require that you give credit to the primary authors below, and that you notify us on github of any improvements you make. +The authors encourage you to redistribute this content as widely as possible, but require that you give credit to the primary authors below, and that you notify us on GitHub of any improvements you make. ### What are Best Practices @@ -52,7 +52,7 @@ Remember [what we mean by _Best Practices_](#what-are-best-practices) The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting [https://github.com/PoshCode/PowerShellPracticeAndStyle](https://github.com/PoshCode/PowerShellPracticeAndStyle) -The *PowerShell Style Guide* in particular is in PREVIEW, and we are still actively working out our disagreements about the rules in the guide through the github issues system. +The *PowerShell Style Guide* in particular is in PREVIEW, and we are still actively working out our disagreements about the rules in the guide through the GitHub issues system. #### Contributing diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 52469b4..f4a7783 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -69,13 +69,13 @@ If you wish, you may use camelCase for variables within your functions (or modul #### One True Brace Style -This guide recommends the so-called ["One True Brace Style" variant to K&R](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81#issuecomment-285835313), which requires that every braceable _statement_ should have the opening brace on the _end of a line_, and the closing brace at the _beginning of a line_. +This guide recommends the so-called ["One True Brace Style" variant to K&R](https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81#issuecomment-285835313), which requires that every braceable _statement_ should have the opening brace on the _end of a line_, and the closing brace at the _beginning of a line_. There is one notable exception when passing small scriptblocks to parameters (where K&R would allow leaving off the braces entirely), we allow putting the entire statement on a single line. ```powershell enum Color { - Black, + Black, White } @@ -88,7 +88,7 @@ function Test-Code { if (10 -gt $ParameterOne) { "Greater" } else { - "Lesser" + "Lesser" } } } @@ -151,11 +151,11 @@ function Test-Code { Limit lines to 115 characters when possible. -Keeping lines to a small width allows scripts to be read in _one_ direction (top to bottom) without scrolling back-and-forth horizontally. What, exactly, this width should be is a one of the favorite arguing points among developers on the internet (more splintered than emacs vs vi or gnu GPL vs MIT). +Keeping lines to a small width allows scripts to be read in _one_ direction (top to bottom) without scrolling back-and-forth horizontally. What, exactly, this width should be is a one of the favorite arguing points among developers on the internet (more splintered than emacs vs vi or gnu GPL vs MIT). In this guide we use two particular sources for the maximum line width: -The PowerShell console is, by default, 120 characters wide, but it allows only 119 characters on output lines, and when entering multi-line text, PowerShell uses a line continuation prompt: `>>> ` and thus limits your line length to 116 anyway. +The PowerShell console is, by default, 120 characters wide, but it allows only 119 characters on output lines, and when entering multi-line text, PowerShell uses a line continuation prompt: `>>> ` and thus limits your line length to 116 anyway. Github's current maximum line width varies between 121 and 126 depending on your browser and OS (and thus, font). However, the 115 line length suggested by PowerShell would be enough to even allow side-by-side diffs to be displayed without scrolling or wrapping on the current "standard" 1080p monitor. @@ -175,7 +175,7 @@ Surround function and class definitions with _two_ blank lines. Method definitions within a class are surrounded by a single blank line. -Blank lines may be ommitted between a bunch of related one-liners (e.g. empty functions) +Blank lines may be omitted between a bunch of related one-liners (e.g. empty functions) Additional blank lines may be used sparingly to separate groups of related functions, or within functions to indicate logical sections (e.g. before a block comment). @@ -220,7 +220,7 @@ $yesterdaysDate = (Get-Date).AddDays(-$i) #### Spaces around special characters -White-space is (mostly) irrelevant to PowerShell, but its proper use is key to writing easily readable code. +White-space is (mostly) irrelevant to PowerShell, but its proper use is key to writing easily readable code. Use a single space after commas and semicolons, and around pairs of curly braces. @@ -241,7 +241,7 @@ Obviously, these rules should not be applied in such a way as to affect output. PowerShell will not complain about extra semicolons, but they are unnecessary, and can get in the way when code is being edited or copy-pasted. They also result in extra do-nothing edits in source control when someone finally decides to delete them. -They are also unecessary when declaring hashtables if you are already putting each element on it's own line: +They are also unnecessary when declaring hashtables if you are already putting each element on its own line: ```PowerShell # This is the preferred way to declare a hashtable if it extends past one line: diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index e7b7843..19bf843 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -2,7 +2,7 @@ Comments that contradict the code are worse than no comments. Always make a priority of keeping the comments up-to-date when the code changes! -Comments should be in English, and should be complete sentences. If the comment is short, the period at the end can be ommited. +Comments should be in English, and should be complete sentences. If the comment is short, the period at the end can be omitted. Remember that comments should serve to your reasoning and decision-making, not attempt to explain what a command does. With the exception of regular expressions, well-written PowerShell can be pretty self-explanatory. From 7a5b132d05d236036fee5fe26b2af70fbb06709e Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Wed, 27 Feb 2019 12:11:14 -0500 Subject: [PATCH 048/113] Fix minor typo s/pojects/projects/ Fix #125 --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 52469b4..644943b 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -159,7 +159,7 @@ The PowerShell console is, by default, 120 characters wide, but it allows only 1 Github's current maximum line width varies between 121 and 126 depending on your browser and OS (and thus, font). However, the 115 line length suggested by PowerShell would be enough to even allow side-by-side diffs to be displayed without scrolling or wrapping on the current "standard" 1080p monitor. -Again, this is a particularly flexible rule, and you should always follow the guidelines of projects when you're contributing to other people's pojects. Although most of us work on widescreen monitors, not everyone can see well without magnification or extremely large fonts. +Again, this is a particularly flexible rule, and you should always follow the guidelines of projects when you're contributing to other people's projects. Although most of us work on widescreen monitors, not everyone can see well without magnification or extremely large fonts. The preferred way to avoid long lines is to use splatting (see [Get-Help about_Splatting](https://technet.microsoft.com/en-us/library/jj672955.aspx)) and PowerShell's implied line continuation inside parentheses, brackets, and braces -- these should **always** be used in preference to the backtick for line continuation when applicable, even for strings: From abafd6ec65a469c88f062c99f63f8924689d2d24 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Wed, 20 Mar 2019 11:51:54 +0000 Subject: [PATCH 049/113] Fix grammatical errors --- Style-Guide/Code-Layout-and-Formatting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 0394875..d1ff5f2 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -98,7 +98,7 @@ Get-ChildItem | Where-Object { $_.Length -gt 10mb } ``` The primary reason for this recommendation is practical: there are no exceptions necessary when following this rule, and when code is written following this style, _new lines_ of code can be inserted between any two lines with no risk of accidentally breaking the code by separating braces from their statement blocks. Thus, it's easier to follow, and makes errors less likely. -Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some addition reasonning here: First: in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second: PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. +Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some addition reasoning here: First: in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second: PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. #### Always Start With CmdletBinding @@ -113,7 +113,7 @@ end { } ``` -You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and necessary valiation and so on, but you should **avoid** writing scripts or functions without `[CmdletBinding()]`, and you should always at least _consider_ making it take pipeline input. +You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and necessary validation and so on, but you should **avoid** writing scripts or functions without `[CmdletBinding()]`, and you should always at least _consider_ making it take pipeline input. #### Prefer: param(), begin, process, end From af347d199238569eb9f8d95937a8358345d55a3e Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Wed, 20 Mar 2019 11:53:22 +0000 Subject: [PATCH 050/113] Fixed markdown best practices --- Style-Guide/Code-Layout-and-Formatting.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index d1ff5f2..b63ff4d 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -96,6 +96,7 @@ function Test-Code { # An Exception case: Get-ChildItem | Where-Object { $_.Length -gt 10mb } ``` + The primary reason for this recommendation is practical: there are no exceptions necessary when following this rule, and when code is written following this style, _new lines_ of code can be inserted between any two lines with no risk of accidentally breaking the code by separating braces from their statement blocks. Thus, it's easier to follow, and makes errors less likely. Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some addition reasoning here: First: in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second: PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. @@ -155,7 +156,7 @@ Keeping lines to a small width allows scripts to be read in _one_ direction (top In this guide we use two particular sources for the maximum line width: -The PowerShell console is, by default, 120 characters wide, but it allows only 119 characters on output lines, and when entering multi-line text, PowerShell uses a line continuation prompt: `>>> ` and thus limits your line length to 116 anyway. +The PowerShell console is, by default, 120 characters wide, but it allows only 119 characters on output lines, and when entering multi-line text, PowerShell uses a line continuation prompt: `>>>` and thus limits your line length to 116 anyway. Github's current maximum line width varies between 121 and 126 depending on your browser and OS (and thus, font). However, the 115 line length suggested by PowerShell would be enough to even allow side-by-side diffs to be displayed without scrolling or wrapping on the current "standard" 1080p monitor. From 89f27bcbac9bb9a1c1d1740bca19cb4f51426b83 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Wed, 20 Mar 2019 12:01:44 +0000 Subject: [PATCH 051/113] Markdown fixes --- Style-Guide/Function-Structure.md | 81 ++++++++++++++++--------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/Style-Guide/Function-Structure.md b/Style-Guide/Function-Structure.md index dcc3fd9..5749679 100644 --- a/Style-Guide/Function-Structure.md +++ b/Style-Guide/Function-Structure.md @@ -3,7 +3,7 @@ Avoid using the `return` keyword in your functions. Just place the object variable on its own. When declaring simple functions leave a space between the function name and the parameters. - + ```PowerShell function MyFunction ($param1, $param2) { ... @@ -12,14 +12,14 @@ function MyFunction ($param1, $param2) { ### Advanced Functions -For Advanced Functions and scripts use the format of **** for +For Advanced Functions and scripts use the format of **\-\** for naming. For a list of approved verbs the cmdlet `Get-Verb` will list them. On the noun side it can be composed of more than one joined word using Pascal Case and only singular nouns. In Advanced Functions do not use the keyword `return` to return an object. -In Advanced Functions you return objects inside the `Process {}` block +In Advanced Functions you return objects inside the `Process {}` block and not in `Begin {}` or `End {}` since it defeats the advantage of the pipeline. ```PowerShell @@ -82,7 +82,7 @@ function Get-USCitizenCapability { #### Always have at least a `process {}` code block if any parameters takes values from the Pipeline. -#### Specify an OutputType attribute if the advanced function returns an object or collection of objects. +#### Specify an OutputType attribute if the advanced function returns an object or collection of objects. If the function returns different object types depending on the parameter set provide one per parameter set. @@ -98,7 +98,7 @@ function Get-User { [CmdletBinding(DefaultParameterSetName = "ID")] [OutputType("System.Int32", ParameterSetName = "ID")] [OutputType([String], ParameterSetName = "Name")] - param ( + param ( [parameter(Mandatory = $true, ParameterSetName = "ID")] [Int[]] $UserID, @@ -106,14 +106,14 @@ function Get-User { [parameter(Mandatory = $true, ParameterSetName = "Name")] [String[]] $UserName - ) + ) <# function body #> } ``` #### When using advanced functions or scripts with CmdletBinding attribute avoid validating parameters in the body of the script when possible and use parameter validation attributes instead. - * **AllowNull** Validation Attribute +* **AllowNull** Validation Attribute The AllowNull attribute allows the value of a mandatory parameter to be null ($null). @@ -123,10 +123,10 @@ function Get-User { [AllowNull()] [String] $ComputerName - ) + ) ``` - * **AllowEmptyString** Validation Attribute +* **AllowEmptyString** Validation Attribute The AllowEmptyString attribute allows the value of a mandatory parameter to be an empty string (""). @@ -136,10 +136,10 @@ function Get-User { [AllowEmptyString()] [String] $ComputerName - ) + ) ``` - * **AllowEmptyCollection** Validation Attribute +* **AllowEmptyCollection** Validation Attribute The AllowEmptyCollection attribute allows the value of a mandatory parameter to be an empty collection (@()). @@ -149,15 +149,15 @@ function Get-User { [AllowEmptyCollection()] [String[]] $ComputerName - ) + ) ``` - * **ValidateCount** Validation Attribute +* **ValidateCount** Validation Attribute The ValidateCount attribute specifies the minimum and maximum number of parameter values that a parameter accepts. Windows PowerShell generates an error if the number of parameter values in the command that - calls the function is outside that range. + calls the function is outside that range. ```PowerShell param ( @@ -165,12 +165,12 @@ function Get-User { [ValidateCount(1,5)] [String[]] $ComputerName - ) + ) ``` - * **ValidateLength** Validation Attribute +* **ValidateLength** Validation Attribute - The ValidateLength attribute specifies the minimum and maximum number + The ValidateLength attribute specifies the minimum and maximum number of characters in a parameter or variable value. Windows PowerShell generates an error if the length of a value specified for a parameter or a variable is outside of the range. @@ -181,39 +181,41 @@ function Get-User { [ValidateLength(1,10)] [String[]] $ComputerName - ) + ) ``` - * **ValidatePattern** Validation Attribute +* **ValidatePattern** Validation Attribute The ValidatePattern attribute specifies a regular expression that is compared to the parameter or variable value. Windows PowerShell generates - an error if the value does not match the regular expression - pattern. + an error if the value does not match the regular expression + pattern. + ```PowerShell param ( [Parameter(Mandatory = $true)] [ValidatePattern("[0-9][0-9][0-9][0-9]")] [String[]] $ComputerName - ) + ) ``` - * **ValidateRange** Validation Attribute +* **ValidateRange** Validation Attribute The ValidateRange attribute specifies a numeric range for each parameter or variable value. Windows PowerShell generates an error - if any value is outside that range. + if any value is outside that range. + ```PowerShell param ( [Parameter(Mandatory = $true)] [ValidateRange(0,10)] [Int] $Attempts - ) + ) ``` - * **ValidateScript** Validation Attribute +* **ValidateScript** Validation Attribute The ValidateScript attribute specifies a script that is used to validate a parameter or variable value. Windows PowerShell @@ -230,12 +232,12 @@ function Get-User { [ValidateScript({$_ -ge (get-date)})] [DateTime] $EventDate - ) + ) ``` - * **ValidateSet** Attribute +* **ValidateSet** Attribute - The ValidateSet attribute specifies a set of valid values for a + The ValidateSet attribute specifies a set of valid values for a parameter or variable. Windows PowerShell generates an error if a parameter or variable value does not match a value in the set. In the following example, the value of the Detail parameter can only @@ -247,36 +249,38 @@ function Get-User { [ValidateSet("Low", "Average", "High")] [String[]] $Detail - ) + ) ``` - * **ValidateNotNull** Validation Attribute +* **ValidateNotNull** Validation Attribute The ValidateNotNull attribute specifies that the parameter value cannot be null ($null). Windows PowerShell generates an - error if the parameter value is null. + error if the parameter value is null. The ValidateNotNull attribute is designed to be used when the type of the parameter value is not specified or when the specified type will accept a value of Null. (If you specify a type that will not accept a null value, such as a string, the null value will be rejected without the ValidateNotNull attribute, because it does not - match the specified type.) + match the specified type.) + ```PowerShell param ( [Parameter(Mandatory = $true)] [ValidateNotNull()] $ID - ) + ) ``` - * **ValidateNotNullOrEmpty** Validation Attribute +* **ValidateNotNullOrEmpty** Validation Attribute - The ValidateNotNullOrEmpty attribute specifies that the parameter + The ValidateNotNullOrEmpty attribute specifies that the parameter value cannot be null ($null) and cannot be an empty string (""). - Windows PowerShell generates an error if the parameter is used in + Windows PowerShell generates an error if the parameter is used in a function call, but its value is null, an empty string, or an empty - array. + array. + ```PowerShell param ( [Parameter(Mandatory = $true)] @@ -285,4 +289,3 @@ function Get-User { $UserName ) ``` - From 9b243fced7525134515abc42a86230bd1c3c27a9 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 09:39:30 +0000 Subject: [PATCH 052/113] Grammatical/Markdown fixes --- Style-Guide/Documentation-and-Comments.md | 70 +++++++++++------------ 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index 51fb87b..e68f0b0 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -1,8 +1,8 @@ ### Documenting and Comments -Comments that contradict the code are worse than no comments. Always make a priority of keeping the comments up-to-date when the code changes! +Comments that contradict the code are worse than no comments. Always make a priority of keeping the comments up-to-date when the code changes! -Comments should be in English, and should be complete sentences. If the comment is short, the period at the end can be omitted. +Comments should be in English, and should be complete sentences. If the comment is short, the period at the end can be omitted. Remember that comments should serve to your reasoning and decision-making, not attempt to explain what a command does. With the exception of regular expressions, well-written PowerShell can be pretty self-explanatory. @@ -14,28 +14,27 @@ $Margin = $Margin + 2 # Maybe write: # The rendering box obscures a couple of pixels. $Margin = $Margin + 2 - ``` #### Block comments Don't go overboard with comments. Unless your code is particularly obscure, don't precede each line with a comment -- doing so breaks up the code and makes it harder to read. Instead, write a single block comment. -Block comments generally apply to some or all of the code which follows them, and are indented to the same level as that code. Each line should start with a # and a single space. +Block comments generally apply to some or all of the code which follows them, and are indented to the same level as that code. Each line should start with a # and a single space. If the block is particularly long (as in the case of documentation text) it is recommended to use the `<# ... #>` block comment syntax, but you should place the comment characters on their own lines, and indent the comment: - + ```PowerShell - # Requiring a space makes things legible and prevents confusion. - # Writing comments one-per line makes them stand out more in the console. +# Requiring a space makes things legible and prevents confusion. +# Writing comments one-per line makes them stand out more in the console. - <# - .SYNOPSIS - Really long comment blocks are tedious to keep commented in single-line mode - .DESCRIPTION +<# + .SYNOPSIS + Really long comment blocks are tedious to keep commented in single-line mode. + .DESCRIPTION Particularly when the comment must be frequently edited, - as with the help and documentation for a function or script - #> + as with the help and documentation for a function or script. +#> ``` #### Inline comments @@ -47,16 +46,16 @@ They should be separated from the code statement by at least two spaces, and ide ```PowerShell $Options = @{ Margin = 2 # The rendering box obscures a couple of pixels. - Padding = 2 # We need space between the border and the text - FontSize = 24 # Keep this above 16 so it's readable in presentations + Padding = 2 # We need space between the border and the text. + FontSize = 24 # Keep this above 16 so it's readable in presentations. } ``` #### Documentation comments -Comment-based help should be written in simple language. +Comment-based help should be written in simple language. -You're not writing a thesis for your college Technical Writing class - you're writing something that describes how a function works. Avoid unecessarily large words, and keep your explanations short. You're not trying to impress anyone, and the only people who will ever read this are just trying to figure out how to use the function. +You're not writing a thesis for your college Technical Writing class - you're writing something that describes how a function works. Avoid unnecessarily large words, and keep your explanations short. You're not trying to impress anyone, and the only people who will ever read this are just trying to figure out how to use the function. If you're writing in what is, for you, a foreign language, simpler words and simpler sentence structures are better, and more likely to make sense to a native reader. @@ -78,17 +77,17 @@ Every script function command should have at least a short statement describing ##### Document Each Parameter -Each parameter should be documented. To make it easier to keep the comments synchronized with changes to the parameters, the preferred location for parameter documentation comments is _within_ the `param` block, directly above each parameter. +Each parameter should be documented. To make it easier to keep the comments synchronized with changes to the parameters, the preferred location for parameter documentation comments is _within_ the `param` block, directly above each parameter. Examples can be found in the ISE snippets: -```powershell +```PowerShell Param( # Param1 help description [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] $Param1, - +     # Param2 help description [int] $Param2 @@ -101,16 +100,16 @@ It is also possible to write `.PARAMETER` statements with the rest of the docume Your help should always provide an example for each major use case. A 'usage example' is just an example of what you would type in to Powershell to run the script - you can even cut and paste one from the command line while you're testing your function. - ```PowerShell function Test-Help { <# .SYNOPSIS - An example function to display how help should be written + An example function to display how help should be written. + .EXAMPLE Get-Help -Name Test-Help - This shows the help for the example function + This shows the help for the example function. #> [CmdletBinding()] param( @@ -125,9 +124,6 @@ function Test-Help { } ``` - - - ### DOC-01 Write comment-based help You should always write comment-based help in your scripts and functions. @@ -135,7 +131,7 @@ You should always write comment-based help in your scripts and functions. Comment-based help is formatted as follows: ```PowerShell -function get-example { +function Get-Example { <# .SYNOPSIS A brief description of the function or script. @@ -144,34 +140,34 @@ function get-example { A longer description. .PARAMETER FirstParameter - Description of each of the parameters + Description of each of the parameters. Note: -        To make it easier to keep the comments synchronized with changes to the parameters, - the preferred location for parameter documentation comments is not here, +        To make it easier to keep the comments synchronized with changes to the parameters, + the preferred location for parameter documentation comments is not here, but within the param block, directly above each parameter. .PARAMETER SecondParameter - Description of each of the parameters + Description of each of the parameters. .INPUTS - Description of objects that can be piped to the script + Description of objects that can be piped to the script. .OUTPUTS - Description of objects that are output by the script + Description of objects that are output by the script. .EXAMPLE - Example of how to run the script + Example of how to run the script. .LINK - Links to further documentation + Links to further documentation. .NOTES - Detail on what the script does, if this is needed + Detail on what the script does, if this is needed. #> ``` -Comment-based help is displayed when the user types `help get-example` or `get-example -?`, etc. +Comment-based help is displayed when the user types `help Get-Example` or `Get-Example -?`, etc. Your help should be helpful. That is, if you've written a tool called `Get-LOBAppUser`, don't write help that merely says, "Gets LOB App Users." Duh. From c988e6442cac2a59f44111527af00669c75f6ec7 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 09:43:29 +0000 Subject: [PATCH 053/113] Markdown fix --- Style-Guide/Readability.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Style-Guide/Readability.md b/Style-Guide/Readability.md index 4179485..16605f2 100644 --- a/Style-Guide/Readability.md +++ b/Style-Guide/Readability.md @@ -34,7 +34,6 @@ foreach ($computer in $computers) { You will probably be reviled if you don't format carefully. - # READ-02 Avoid backticks Consider this: From 590da989dd15bf5df63b6d4fa2c5f7cdaf94c69a Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 09:48:18 +0000 Subject: [PATCH 054/113] Markdown fixes --- Style-Guide/Naming-Conventions.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index a0fc205..a8072dc 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -14,7 +14,7 @@ gps -Name Explorer Get-Process -Name Explorer ``` -#### Use full parameter names. +#### Use full parameter names. Because there are so many commands in PowerShell, it's impossible for every scripter to know every command. Therefore it's useful to be explicit about your parameter names for the sake of readers who may be unfamiliar with the command you're using. This will also help you avoid bugs if a future change to the command alters the parameter sets. @@ -44,7 +44,7 @@ Get-Content (Join-Path $PSScriptRoot README.md) [System.IO.File]::ReadAllText("$PSScriptRoot\README.md") ``` -##### Avoid the use of `~` to represent the home folder. +##### Avoid the use of `~` to represent the home folder. The meaning of ~ is unfortunately dependent on the "current" provider at the time of execution. This isn't really a style issue, but it's an important rule for code you intend to share anyway. Instead, use `${Env:UserProfile}` or `(Get-PSProvider FileSystem).Home` ... @@ -58,6 +58,4 @@ At line:1 char:1 + ~~~~ + CategoryInfo : InvalidOperation: (:) [Set-Location], PSInvalidOperationException + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.SetLocationCommand - ``` - From 0ba81dcfad6771900e4812ba7aec2c24c6c561e7 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 09:49:34 +0000 Subject: [PATCH 055/113] Grammatical/markdown fixes --- Style-Guide/Introduction.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Style-Guide/Introduction.md b/Style-Guide/Introduction.md index 6cd1366..2839065 100644 --- a/Style-Guide/Introduction.md +++ b/Style-Guide/Introduction.md @@ -4,7 +4,7 @@ In the Python community, developers have a great programming style reference provided as part of the language enhancement process specifications ([PEP-8](https://www.python.org/dev/peps/pep-0008/)), but in the PowerShell world there has been no official documentation of community preferences. -This document is an attempt to come to an agreement on a style-guide because we know that the more people follow the same set of code-style habits, the more readable the community's code will be. In other words, although the recommendations of this guide are _just recomendations_, if you follow them, you will write PowerShell code that is more easily read, understood, and maintained. +This document is an attempt to come to an agreement on a style-guide because we know that the more people follow the same set of code-style habits, the more readable the community's code will be. In other words, although the recommendations of this guide are _just recommendations_, if you follow them, you will write PowerShell code that is more easily read, understood, and maintained. ## Table of Contents @@ -13,4 +13,3 @@ This document is an attempt to come to an agreement on a style-guide because we - [Documentation and Comments](Documentation-and-Comments.md) - [Readability](Readability.md) - [Naming Conventions](Naming-Conventions.md) - From 6477e653430d8292cb6e374c502d66498594eb6e Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 09:54:11 +0000 Subject: [PATCH 056/113] Added missing parameter name --- Style-Guide/Naming-Conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index a8072dc..b855d46 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -46,7 +46,7 @@ Get-Content (Join-Path $PSScriptRoot README.md) ##### Avoid the use of `~` to represent the home folder. -The meaning of ~ is unfortunately dependent on the "current" provider at the time of execution. This isn't really a style issue, but it's an important rule for code you intend to share anyway. Instead, use `${Env:UserProfile}` or `(Get-PSProvider FileSystem).Home` ... +The meaning of ~ is unfortunately dependent on the "current" provider at the time of execution. This isn't really a style issue, but it's an important rule for code you intend to share anyway. Instead, use `${Env:UserProfile}` or `(Get-PSProvider -PSProvider FileSystem).Home` ... ```PowerShell PS C:\Windows\system32> cd ~ From c231b4707d0f28483406292e9b4b282e5ed3b4c2 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 09:56:01 +0000 Subject: [PATCH 057/113] Added missing parameter names --- Style-Guide/Naming-Conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index b855d46..4ceda52 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -38,7 +38,7 @@ Get-Content .\README.md [System.IO.File]::ReadAllText(".\README.md") # Instead write: -Get-Content (Join-Path $PSScriptRoot README.md) +Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath README.md) # Or even use string concatenation: [System.IO.File]::ReadAllText("$PSScriptRoot\README.md") From dbacd199fb550ccefc2b5c8811d0f9ba81fe84d6 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 10:05:28 +0000 Subject: [PATCH 058/113] Minor fixes --- Style-Guide/Code-Layout-and-Formatting.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index b63ff4d..2daee33 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -29,13 +29,13 @@ PowerShell language keywords are written in lower case (yes, even `foreach` and function Write-Host { <# .SYNOPSIS - Writes customized output to a host. + Writes customized output to a host. .DESCRIPTION - The Write-Host cmdlet customizes output. You can specify the color of text by using - the ForegroundColor parameter, and you can specify the background color by using the - BackgroundColor parameter. The Separator parameter lets you specify a string to use to - separate displayed objects. The particular result depends on the program that is - hosting Windows PowerShell. + The Write-Host cmdlet customizes output. You can specify the color of text by using + the ForegroundColor parameter, and you can specify the background color by using the + BackgroundColor parameter. The Separator parameter lets you specify a string to use to + separate displayed objects. The particular result depends on the program that is + hosting Windows PowerShell. #> [CmdletBinding()] param( @@ -165,9 +165,11 @@ Again, this is a particularly flexible rule, and you should always follow the gu The preferred way to avoid long lines is to use splatting (see [Get-Help about_Splatting](https://technet.microsoft.com/en-us/library/jj672955.aspx)) and PowerShell's implied line continuation inside parentheses, brackets, and braces -- these should **always** be used in preference to the backtick for line continuation when applicable, even for strings: ```powershell -Write-Host ("This is an incredibly important, and extremely long message. " + - "We cannot afford to leave any part of it out, nor do we want line-breaks in the output. " + - "Using string concatenation lets us use short lines here, and still get a long line in the output") +Write-Host -InputObject ("This is an incredibly important, and extremely long message. " + + "We cannot afford to leave any part of it out, " + + "nor do we want line-breaks in the output. " + + "Using string concatenation lets us use short lines here, " + + "and still get a long line in the output") ``` #### Blank Lines and Whitespace @@ -215,7 +217,7 @@ $yesterdaysDate = (Get-Date).AddDays(-1) $i = 0 $i++ -# Same principle should be applied when using a variable +# Same principle should be applied when using a variable. $yesterdaysDate = (Get-Date).AddDays(-$i) ``` From 702ae2a0e7f589d6ecf3e1a3fd2e1ba29da4fa69 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 10:13:10 +0000 Subject: [PATCH 059/113] fixed casing on param --- Style-Guide/Documentation-and-Comments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index e68f0b0..af383ef 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -81,7 +81,7 @@ Each parameter should be documented. To make it easier to keep the comments sync Examples can be found in the ISE snippets: ```PowerShell -Param( +param( # Param1 help description [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, From f2cbaa3b309e1069b9d61060753b63efa4b17a03 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 10:19:26 +0000 Subject: [PATCH 060/113] Fixed parameter name --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 2daee33..75275f2 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -165,7 +165,7 @@ Again, this is a particularly flexible rule, and you should always follow the gu The preferred way to avoid long lines is to use splatting (see [Get-Help about_Splatting](https://technet.microsoft.com/en-us/library/jj672955.aspx)) and PowerShell's implied line continuation inside parentheses, brackets, and braces -- these should **always** be used in preference to the backtick for line continuation when applicable, even for strings: ```powershell -Write-Host -InputObject ("This is an incredibly important, and extremely long message. " + +Write-Host -Object ("This is an incredibly important, and extremely long message. " + "We cannot afford to leave any part of it out, " + "nor do we want line-breaks in the output. " + "Using string concatenation lets us use short lines here, " + From f355366820569e947969e454f5e34c5efdb9a4f0 Mon Sep 17 00:00:00 2001 From: Bailey Lawson <41685901+BaileyLawson@users.noreply.github.com> Date: Thu, 21 Mar 2019 10:22:06 +0000 Subject: [PATCH 061/113] Removed extra spaces --- Style-Guide/Function-Structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Function-Structure.md b/Style-Guide/Function-Structure.md index 5749679..9af1e8b 100644 --- a/Style-Guide/Function-Structure.md +++ b/Style-Guide/Function-Structure.md @@ -111,7 +111,7 @@ function Get-User { } ``` -#### When using advanced functions or scripts with CmdletBinding attribute avoid validating parameters in the body of the script when possible and use parameter validation attributes instead. +#### When using advanced functions or scripts with CmdletBinding attribute avoid validating parameters in the body of the script when possible and use parameter validation attributes instead. * **AllowNull** Validation Attribute From 7a771b15a59134358b64b52420dd311ce4ef3daf Mon Sep 17 00:00:00 2001 From: jdgregson Date: Tue, 2 Apr 2019 11:49:40 -0700 Subject: [PATCH 062/113] Replaced "param()" with "param ()" for consistency. Most examples in this project use the `param` keywork with a space before the parentheses, like `param (` or `param ()`. However, some examples omit the space, or capitalize the P, like `param(` or `Param(`. This is a needless inconsistency, which this PR addresses. Note that two instances of `param()` are left in tact, since they are in a TODO file. --- Style-Guide/Code-Layout-and-Formatting.md | 8 ++++---- Style-Guide/Documentation-and-Comments.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 0394875..954aed0 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -38,7 +38,7 @@ function Write-Host { hosting Windows PowerShell. #> [CmdletBinding()] - param( + param ( [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromRemainingArguments = $true)] [PSObject] $Object, @@ -81,7 +81,7 @@ enum Color { function Test-Code { [CmdletBinding()] - param( + param ( [int]$ParameterOne ) end { @@ -106,7 +106,7 @@ All of your scripts or functions should start life as something like this snippe ```powershell [CmdletBinding()] -param() +param () process { } end { @@ -115,7 +115,7 @@ end { You can always delete or ignore one of the blocks (or add the `begin` block), add parameters and necessary valiation and so on, but you should **avoid** writing scripts or functions without `[CmdletBinding()]`, and you should always at least _consider_ making it take pipeline input. -#### Prefer: param(), begin, process, end +#### Prefer: param (), begin, process, end Having a script written in the order of execution makes the intent clearer. Since there is no functional reason to have these blocks out of order (they _will_ still be executed in the normal order), writing them out of order can be confusing, and makes code more difficult to maintain and debug. diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index 51fb87b..7cdf8fd 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -82,7 +82,7 @@ Each parameter should be documented. To make it easier to keep the comments sync Examples can be found in the ISE snippets: ```powershell -Param( +param ( # Param1 help description [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, @@ -113,7 +113,7 @@ function Test-Help { This shows the help for the example function #> [CmdletBinding()] - param( + param ( # This parameter doesn't do anything. # Aliases: MP [Parameter(Mandatory = $true)] From 9ea52ca2e83bcddd695976ddbd36df495809be41 Mon Sep 17 00:00:00 2001 From: TheIncorrigible1 Date: Wed, 17 Apr 2019 10:25:31 -0400 Subject: [PATCH 063/113] addressed style guide inconsistencies --- Best-Practices/Performance.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Best-Practices/Performance.md b/Best-Practices/Performance.md index 8c93574..bb88ea1 100644 --- a/Best-Practices/Performance.md +++ b/Best-Practices/Performance.md @@ -5,7 +5,7 @@ PowerShell comes equipped with 3.2 million performance quirks. Approximately. For example, the first line below executes a lot faster than the second: ```PowerShell -[void]Do-Something +[void](Do-Something) Do-Something | Out-Null ``` @@ -20,10 +20,10 @@ This is an important area for people in the PowerShell community. While everyone For example: ```PowerShell -$content = Get-Content file.txt +$content = Get-Content -Path file.txt -ForEach ($line in $content) { - Do-Something -input $line +foreach ($line in $content) { + Do-Something -Input $line } ``` @@ -32,9 +32,9 @@ Most folks will agree that the basic aesthetics of that example are good. This s Now consider this alternate approach: ```PowerShell -Get-Content file.txt | +Get-Content -Path file.txt | ForEach-Object -Process { - Do-Something -input $\_ + Do-Something -Input $_ } ``` @@ -43,25 +43,25 @@ As described elsewhere in this guide, many folks in the community would dislike Some would argue that this second approach is always a poor one, and that if performance is an issue then you should devolve from a PowerShell-native approach into a lower-level .NET Framework approach: ```PowerShell -$sr = New-Object -Type System.IO.StreamReader -Arg file.txt +$sr = New-Object -TypeName System.IO.StreamReader -ArgumentList file.txt while ($sr.Peek() -ge 0) { - $line = $sr.ReadLine() - Do-Something -input $line + $line = $sr.ReadLine() + Do-Something -Input $line } ``` There are myriad variations to this approach, of course, but it solves the performance problem by reading one line at a time, instead of buffering the entire file into memory. It maintains the structured programming approach of the first example, at the expense of using a potentially harder-to-follow .NET Framework model instead of native PowerShell commands. Many regard this third example as an intermediate step, and suggest that a truly beneficial approach would be to write PowerShell commands as "wrappers" around the .NET code. For example (noting that this fourth example uses fictional commands by way of illustration): ```PowerShell -$handle = Open-TextFile file.txt +$handle = Open-TextFile -Path file.txt -while (-not Test-TextFile -handle $handle) { - Do-Something -input (Read-TextFile -handle $handle) +while (-not (Test-TextFile -Handle $handle)) { + Do-Something -Input (Read-TextFile -Handle $handle) } ``` -This example reverts back to a native PowerShell approach, using commands and parameters. The proposed commands (Open-TextFile, Test-TextFile, and Read-TextFile) are just wrappers around .NET Framework classes, such as the StreamReader class shown in the third example. +This example reverts back to a native PowerShell approach, using commands and parameters. The proposed commands (`Open-TextFile`, `Test-TextFile`, and `Read-TextFile`) are just wrappers around .NET Framework classes, such as the StreamReader class shown in the third example. You will generally find that it is possible to conform with the community's general aesthetic preferences while still maintaining a good level of performance. Doing so may require more work - such as writing PowerShell wrapper commands around underlying .NET Framework classes. Most would argue that, for a tool that is intended for long-term use, the additional work is a worthwhile investment. From a9dad38cfb98d3a5416ce0409bfd3406060fa470 Mon Sep 17 00:00:00 2001 From: Ryan <48040509+Ryan-P-Walsh@users.noreply.github.com> Date: Wed, 5 Jun 2019 18:02:28 +1000 Subject: [PATCH 064/113] Spelling fix for parameter 'Wait' in example Simple spelling fix --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 75275f2..81c3e32 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -196,7 +196,7 @@ One notable exception is when using colons to pass values to switch parameters: ```PowerShell # Do not write: -$variable=Get-Content $FilePath -Wai:($ReadCount-gt0) -First($ReadCount*5) +$variable=Get-Content $FilePath -Wait:($ReadCount-gt0) -First($ReadCount*5) # Instead write: $variable = Get-Content -Path $FilePath -Wait:($ReadCount -gt 0) -TotalCount ($ReadCount * 5) From 9bcda6675dc75fe99efc84d557e0f9e9d5c338b5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 26 Jul 2019 07:44:45 -0700 Subject: [PATCH 065/113] 'wai' to 'wait' in code-layout-and-formatting --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 75275f2..81c3e32 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -196,7 +196,7 @@ One notable exception is when using colons to pass values to switch parameters: ```PowerShell # Do not write: -$variable=Get-Content $FilePath -Wai:($ReadCount-gt0) -First($ReadCount*5) +$variable=Get-Content $FilePath -Wait:($ReadCount-gt0) -First($ReadCount*5) # Instead write: $variable = Get-Content -Path $FilePath -Wait:($ReadCount -gt 0) -TotalCount ($ReadCount * 5) From 48eb9ed2d16531cb4c63e43f207e670e14247c07 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Sat, 17 Oct 2020 00:09:59 -0400 Subject: [PATCH 066/113] Fix bug reporting link --- Best-Practices/Building-Reusable-Tools.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Best-Practices/Building-Reusable-Tools.md b/Best-Practices/Building-Reusable-Tools.md index ff3bacf..964fe7f 100644 --- a/Best-Practices/Building-Reusable-Tools.md +++ b/Best-Practices/Building-Reusable-Tools.md @@ -80,8 +80,7 @@ On the flip side, it's important to note that writing your own code from the gro # WAST-02 Report bugs to Microsoft -An exception: if you know that a built-in technique doesn't work properly (e.g., it is buggy or doesn't accomplish the exact task), then obviously it's fine to re-invent the wheel. However, in cases where you're doing so to avoid a bug or design flaw, then you should - as an upstanding member of the community - report the bug on [connect.microsoft.com](http://connect.microsoft.com/) also. - +An exception: if you know that a built-in technique doesn't work properly (e.g., it is buggy or doesn't accomplish the exact task), then obviously it's fine to re-invent the wheel. However, in cases where you're doing so to avoid a bug or design flaw, then you should - as an upstanding member of the community - report the bug on [github.com/powershell](https://github.com/PowerShell/PowerShell/issues) also. TODO: The "PURE" section is dubious at best. We need to discuss it. From 789c2382e401ac941334c6fceee30d55c51da3d6 Mon Sep 17 00:00:00 2001 From: Thomas Clarke Date: Wed, 12 May 2021 08:24:08 +0100 Subject: [PATCH 067/113] Update `Expand-Alias` link relating to issue #149 --- Style-Guide/Naming-Conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index 4ceda52..fab271a 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -1,6 +1,6 @@ ### Naming Conventions -In general, prefer the use of full explicit names for commands and parameters rather than aliases or short forms. There are tools [Expand-Alias](https://github.com/PoshCode/ModuleBuilder/blob/master/ResolveAlias.psm1) for fixing many, but not all of these issues. +In general, prefer the use of full explicit names for commands and parameters rather than aliases or short forms. There are tools [Expand-Alias](https://github.com/PoshCode/ModuleBuilder/blob/master/PotentialContribution/ResolveAlias.psm1) for fixing many, but not all of these issues. #### Use the full name of each command. From dbbaf791684bdb68fcc6f80b9d247d9881c7d35b Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Sat, 28 Aug 2021 17:27:10 -0400 Subject: [PATCH 068/113] Add PSScriptAnalyzer for fixing naming conventions --- Style-Guide/Naming-Conventions.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index fab271a..26b2e3f 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -1,6 +1,6 @@ ### Naming Conventions -In general, prefer the use of full explicit names for commands and parameters rather than aliases or short forms. There are tools [Expand-Alias](https://github.com/PoshCode/ModuleBuilder/blob/master/PotentialContribution/ResolveAlias.psm1) for fixing many, but not all of these issues. +In general, prefer the use of full explicit names for commands and parameters rather than aliases or short forms. There are tools like [PSScriptAnalyzer](https://github.com/PowerShell/PSScriptAnalyzer)'s `Invoke-Formatter` and scripts like [Expand-Alias](https://github.com/PoshCode/ModuleBuilder/blob/master/PotentialContribution/ResolveAlias.psm1) for fixing many, but not all of these issues. #### Use the full name of each command. @@ -59,3 +59,10 @@ At line:1 char:1 + CategoryInfo : InvalidOperation: (:) [Set-Location], PSInvalidOperationException + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.SetLocationCommand ``` + + +#### Use lowercase for keywords and operators + +For clarity, we recommend always using lowercase for keywords and operators, as they will be more easily distinguished from non-keyword use of the same text. + +PowerShell is rarely case sensitive, and certainly is not case sensitive about keywords or operators. However, keywords are frequently duplicated by methods and cmdlet names (see for example `foreach` and `ForEach-Object` and `.ForEach(...)`) which are always PascalCase. Operators are also frequently duplicated by parameters (see for example most of the parameters on `Where-Object`). From 591eac112628787e64d7b6d5880de8f10da9ec57 Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Sat, 28 Aug 2021 20:01:57 -0400 Subject: [PATCH 069/113] Additional formatting fixes Removing weasel words --- Style-Guide/Code-Layout-and-Formatting.md | 19 ++++++--------- Style-Guide/Naming-Conventions.md | 28 ++++++++++++++++------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 81b1560..a39790d 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -38,22 +38,17 @@ function Write-Host { hosting Windows PowerShell. #> [CmdletBinding()] - param ( + param( [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromRemainingArguments = $true)] - [PSObject] - $Object, + [psobject]$Object, - [Switch] - $NoNewline, + [switch]$NoNewline, - [PSObject] - $Separator, + [psobject]$Separator, - [System.ConsoleColor] - $ForegroundColor, + [System.ConsoleColor]$ForegroundColor, - [System.ConsoleColor] - $BackgroundColor + [System.ConsoleColor]$BackgroundColor ) begin { ... @@ -166,7 +161,7 @@ The preferred way to avoid long lines is to use splatting (see [Get-Help about_S ```powershell Write-Host -Object ("This is an incredibly important, and extremely long message. " + - "We cannot afford to leave any part of it out, " + + "We cannot afford to leave any part of it out, " + "nor do we want line-breaks in the output. " + "Using string concatenation lets us use short lines here, " + "and still get a long line in the output") diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index 26b2e3f..5afdb43 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -28,7 +28,9 @@ Get-Process -Name Explorer #### Use full, explicit paths when possible. -When writing scripts, it's really only safe to use `..` or `.` in a path if you have previously explicitly set the location (within the script), and even then you should beware of using relative paths when calling .Net methods or legacy/native applications, because they will use the `[Environment]::CurrentDirectory` rather than PowerShell's present working directory (`$PWD`). Because checking for these types of errors is tedious (and because they are easy to over-look) it's best to avoid using relative paths altogether, and instead, base your paths off of $PSScriptRoot (the folder your script is in) when necessary. +When writing scripts, it is only safe to use `..` or `.` in a path if you have previously set the location explicitly (within the current function or script). Even if you _have_ explictly set the path, you must beware of using relative paths when calling .Net methods or legacy/native applications, because they will use `[Environment]::CurrentDirectory` which is not automatically updated to PowerShell's present working directory (`$PWD`). + +Because troubleshooting these types of errors is tedious (and they are easy to over-look) it's best to avoid using relative paths altogether, and instead, base your paths off of $PSScriptRoot (the folder your script is in) when necessary. ```PowerShell # Do not write: @@ -37,11 +39,23 @@ Get-Content .\README.md # Especially do not write: [System.IO.File]::ReadAllText(".\README.md") -# Instead write: + +# Although you can write: +Push-Location $PSScriptRoot +Get-Content README.md + +# It would be better to write: Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath README.md) +# Or to use string concatenation: +Get-Content "$PSScriptRoot\README.md" -# Or even use string concatenation: +# For calling .net methods, pass full paths: [System.IO.File]::ReadAllText("$PSScriptRoot\README.md") + +# Optionally by calling Convert-Path +Push-Location $PSScriptRoot +[System.IO.File]::ReadAllText((Convert-Path README.md)) + ``` ##### Avoid the use of `~` to represent the home folder. @@ -52,7 +66,7 @@ The meaning of ~ is unfortunately dependent on the "current" provider at the tim PS C:\Windows\system32> cd ~ PS C:\Users\Name> cd HKCU:\Software PS HKCU:\Software> cd ~ -cd : Home location for this provider is not set. To set the home location, call "(get-psprovider 'Registry').Home = 'path'". +cd : Home location for this provider is not set. To set the home location, call "(Get-PSProvider 'Registry').Home = 'path'". At line:1 char:1 + cd ~ + ~~~~ @@ -61,8 +75,6 @@ At line:1 char:1 ``` -#### Use lowercase for keywords and operators - -For clarity, we recommend always using lowercase for keywords and operators, as they will be more easily distinguished from non-keyword use of the same text. +#### See also the Capitalization Conventions -PowerShell is rarely case sensitive, and certainly is not case sensitive about keywords or operators. However, keywords are frequently duplicated by methods and cmdlet names (see for example `foreach` and `ForEach-Object` and `.ForEach(...)`) which are always PascalCase. Operators are also frequently duplicated by parameters (see for example most of the parameters on `Where-Object`). +In the Code Layout and Formatting chapter, there is a section on [capitalization conventions](\PowerShellPracticeAndStyle\Style-Guide\Code-Layout-and-Formatting.md#Capitalization-Conventions) which are \ No newline at end of file From d1f0743018d58fc70c22a2777c4a3682b024893d Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Sat, 28 Aug 2021 22:23:14 -0400 Subject: [PATCH 070/113] Adding some thoughts on Writing Parameter Blocks --- Best-Practices/Introduction.md | 1 + Best-Practices/Writing-Parameter-Blocks.md | 130 +++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 Best-Practices/Writing-Parameter-Blocks.md diff --git a/Best-Practices/Introduction.md b/Best-Practices/Introduction.md index b32ef39..bb5720d 100644 --- a/Best-Practices/Introduction.md +++ b/Best-Practices/Introduction.md @@ -16,6 +16,7 @@ One final note about these Best Practices: the perspective of these guidelines h - [Naming Conventions](Naming-Conventions.md) - [Building Reusable Tools](Building-Reusable-Tools.md) +- [Writing Parameter Blocks](Writing-Parameter-Blocks.md) - [Output and Formatting](Output-and-Formatting.md) - [Error Handling](Error-Handling.md) - [Performance](Performance.md) diff --git a/Best-Practices/Writing-Parameter-Blocks.md b/Best-Practices/Writing-Parameter-Blocks.md new file mode 100644 index 0000000..63f3133 --- /dev/null +++ b/Best-Practices/Writing-Parameter-Blocks.md @@ -0,0 +1,130 @@ +# Writing Parameter Blocks + +## Always write Help + +For every script and function you should have a comment-based help block (we recommend using a block comment). The best place for these is _inside_ the `function` above the `param` block, but they can also be placed _above_ the function, or at the bottom just before closing. + +In order for it to register as help, you must provide a `.SYNOPSIS` and/or `.DESCRIPTION` + +### Always Write Examples + +You should also always provide at least one example (even if your function doesn't take parameters), where you show the output (or the need to capture it), and explain what happens when the command is run. Note that examples should have the _code_ first, and the _documentation_ of what it does after an empty line or two. + + +```PowerShell +function Test-Help { + <# + .SYNOPSIS + An example function to display how help should be written. + + .EXAMPLE + Test-Help -Name Test-Help + + This tests the help for the Test-Help function. + #> + [CmdletBinding()] + param ( + # This parameter doesn't do anything, but you must provide a value + # Aliases: MP + [Parameter(Mandatory = $true)] + [Alias("MP")] + [String]$MandatoryParameter + ) + + <# code here ... #> +} +``` +### Always Document Every Parameter + +You should always provide at least a brief explanation of each parameter, what it's expected or allowed values are, etc. + +The best place for this is a simple comment directly above the parameter (inside the param block) so you don't forget to update it if you remove, rename, or otherwise change the parameter, but you can also place them in the comment help block by using `.PARAMETER ParameterName` and writing the help on the next line. + +## You should specify `[CmdletBinding()]` + +CmdletBinding makes functions and scripts behave like the built-in commands, adding support for the "common" output parameters like `-Verbose` and `-ErrorAction` and supporting `-?` for help. If you don't support it, you risk someone _accidentally_ running your code when they were just trying to `Get-Help` on it. + +There are a few specific advanced cases where you might want to write an old-fashioned script that doesn't use CmdletBinding() -- they are very rare, and are all exceptions to the best practices, so we won't go into them further. + +## You should pick a default ParameterSet + +If you have more than one ParameterSetName on your parameters, you should specify one of them as the `DefaultParameterSetName` in the CmdletBinding. + +## You should support --whatif + +If you write a command that changes state, you should probably add `SupportsShouldProcess` to your CmdletBinding. This allows users to specify `-WhatIf` and `-Confirm` when calling your command, so you'll need to actually support those by using `$PSCmdlet.ShouldProcess(...)` or `$PSCmdlet.ShouldContinue(...)` or by passing the preference variable on to other commands you're calling (e.g. `-Whatif:$WhatIfPreference`). + +Here's an example of what that might look like: + +```PowerShell +# NOTE: ConfirmImpact defaults to Medium +# But I recommend setting ConfirmImpact explicitly as a reminder :) +[CmdletBinding(SupportsShouldProcess, ConfirmImpact = "Medium")] +param([switch]$Force) + +# You need to pre-define these (because they're passed by [ref]) +$RejectAll = $false; +$ConfirmAll = $false; + +# Note: please don't actually do this with services, restarting them in non-dependency order would be a nightmare... +foreach ($service in Get-Service | Where Status -eq "Running") { + # This will normally automatically be TRUE. It will only query if the user: + # 1. Has their $ConfirmPreference (default High) set LOWER or equal to the ConfirmImpact in the cmdlet binding (default Medium) + # 2. Passes -Confirm, which sets the $ConfirmPreference in the function's scope to Low + if ($PSCmdlet.ShouldProcess( "Restarted the service '$($service.Name)'", + "Restart the '$($service.DisplayName)' service ($($service.Name))?", + "Restarting Services" )) { + + # If you use ShouldContinue, have a -Force parameter that bypasses it + # And if you know there may be multiple prompts, you should use this overload that supports the Confirm/Reject "All" option + # In this example, we're only prompting when there are dependent services, and otherwise restart without additional prompting + if ($Force -Or $service.DependentServices.Count -eq 0 -or $PSCmdlet.ShouldContinue( + "$($service.Name) has $($service.DependentServices.Count) dependent services. Are you sure?", + "Restarting the '$($service.DisplayName)' service", + [ref]$ConfirmAll, + [ref]$RejectAll)) { + "(Not actually) restarting $($service.DisplayName)" + } + } +} +``` + +## You should strongly type parameters + +Although PowerShell is a dynamic language, we can specify types, and in parameters, it's particularly useful. + + +First, because it hints to users what sort of values they can pass to your command. Is it numeric? Text? An object? + +Second, because using types on parameters helps validate the input, which is crucial because parameters are where you get your user input. Strong types can help you avoid code injection and other problems with user inputs, and will allow failures to happen as early as possible (even before your command is called). + +Additionally, when passing on parameters to another command, you should be _at least_ as strongly typed as the other command, to avoid casting exceptions within your script. + + +### Be careful with `[string]` or `[object]` (and `[PSObject]`) + +Obviously [string] is one of the most common parameter types, and [object] is the default type. However, because anything can be cast to these types, you should avoid combining these types with parameters that are designed to differentiate parameter sets, or that accept `ValueFromPipeline` because PowerShell will coerce _everything_ to that. + +Obviously if you want to accept more than one type of object on the same parameter, you have to use `[object]` or `[PSObject]` as the universal base types. You have to be very careful when doing this, and normally should use a `ValidateScript` to ensure the objects are one of your _actual_ supported types. + +### Using [pscredential] + +When you need to accept credentials, you almost always want to name the parameter `$Credential` and accept a [System.Management.Automation.PSCredential](https://docs.microsoft.com/en-us/dotnet/api/System.Management.Automation.PSCredential), which has special support in PowerShell for automatically coercing user names to credentials and more. + +In old versions of PowerShell, you needed to manually decorate these PSCredential parameters with `[System.Management.Automation.CmdletAttribute()]` in order to automatically coerce user names to credentials -- that is, to support the use case where someone writes `-Credential Jaykul` and is automatically prompted for the password using the secure credential prompt. In current versions, this is automatically added when you use the PSCredential type. + +### Using [switch] + +Parameters of type `[switch]` support passing as switches (without a value), and by default cannot be passed by position. + +- Switch parameters should not be given default values. They should always default to false. +- Switch parameters should be designed so that setting them moves a command from its default functionality to a less common or more complicated mode. +- Switch parameters should be treated as boolean values in your scripts. Corrolary: you should not write logic that depends on whether or not the user explicitly passed a value to a switch -- do not attempt to treat a switch as having three states! +- When you need to pass the value of a switch on to another command, you can either splat it, or specify it using the colon syntax for parameters, as in `-TheirSwitch:$MySwitch` + + +## Be generous with accept ValueFromPipelineByPropertyName + +For the most flexibility, whenever it's practical, you should write your commands to accept their parameters from the pipeline _by property name_. To enhance your ability to match objects, you can add aliases for the parameter name using the `[Alias()]` attribute. + +Don't forget that values set from the pipeline are only available in the `process` block. \ No newline at end of file From 9a58d22178aaca63672952681ef765e4a06ec92e Mon Sep 17 00:00:00 2001 From: Joel Bennett Date: Fri, 17 Dec 2021 01:01:11 -0500 Subject: [PATCH 071/113] Update Performance to clarify per #153 Changed the example (because frankly, it's not true anymore), and added a little more detail in a PERF-03 --- Best-Practices/Performance.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Best-Practices/Performance.md b/Best-Practices/Performance.md index bb88ea1..8657cab 100644 --- a/Best-Practices/Performance.md +++ b/Best-Practices/Performance.md @@ -2,14 +2,16 @@ PowerShell comes equipped with 3.2 million performance quirks. Approximately. -For example, the first line below executes a lot faster than the second: +If you're aware of multiple techniques to accomplish something, and you're writing a production script that will be dealing with large data sets (meaning performance will become a cumulative factor), then test the performance using Measure-Command or the Profiler module, or some other tool. + +For example: ```PowerShell -[void](Do-Something) -Do-Something | Out-Null +foreach($result in Do-Something) { $result.PropertyOne + $result.PropertyTwo } +Do-Something | ForEach-Object { $_.PropertyOne + $_.PropertyTwo } ``` -If you're aware of multiple techniques to accomplish something, and you're writing a production script that will be dealing with large data sets (meaning performance will become a cumulative factor), then test the performance using Measure-Command or some other tool. +In this case, the `foreach` language construct is faster than piping to the `ForEach-Object` cmdlet -- but the point is that you should measure, and do so on the hardware and PowerShell version where the performance matters to you. # PERF-02 Consider trade-offs between performance and readability @@ -66,3 +68,14 @@ This example reverts back to a native PowerShell approach, using commands and pa You will generally find that it is possible to conform with the community's general aesthetic preferences while still maintaining a good level of performance. Doing so may require more work - such as writing PowerShell wrapper commands around underlying .NET Framework classes. Most would argue that, for a tool that is intended for long-term use, the additional work is a worthwhile investment. The moral here is that both aesthetic and performance are important considerations, and without some work context, neither is inherently more important than the other. It is often possible, with the right technique, to satisfy both. As a general practice, you should avoid giving up on aesthetics solely because of performance concerns - when possible, make the effort to satisfy both performance and aesthetics. + +# PERF-03 Language > Framework > Script > Pipeline + +This is just a rough guideline, but as a general rule: + +1. Language features are faster than features of the .net framework +2. Compiled methods on objects and .net classes are still faster than script +3. Simple PowerShell script is still faster than calling functions or cmdlets + +It's counter-intuitive that script is faster than calling cmdlets that are compiled, but it's frequently true, unless there is a lot of work being done by each cmdlet. The overhead of calling cmdlets and passing data around is significant. Of course, this is just a guideline, and you should always **measure**. + From 3c7a41bb3ff4d6e247ccea5bd2bcd444cf62c2f8 Mon Sep 17 00:00:00 2001 From: "Zane D. Purvis" Date: Sun, 13 Feb 2022 02:58:10 -0500 Subject: [PATCH 072/113] Fix link to capitalization conventions --- Style-Guide/Naming-Conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index 5afdb43..b65a14f 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -77,4 +77,4 @@ At line:1 char:1 #### See also the Capitalization Conventions -In the Code Layout and Formatting chapter, there is a section on [capitalization conventions](\PowerShellPracticeAndStyle\Style-Guide\Code-Layout-and-Formatting.md#Capitalization-Conventions) which are \ No newline at end of file +In the Code Layout and Formatting chapter, there is a section on [capitalization conventions](Code-Layout-and-Formatting.md#Capitalization-Conventions). From 8c7b9069ad5c0c4424f28379467af8457e963ccc Mon Sep 17 00:00:00 2001 From: Albin Date: Wed, 27 Jul 2022 08:09:09 -0500 Subject: [PATCH 073/113] Typo: "prividing" to "providing" --- Best-Practices/Language-Interop-and-.Net.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Best-Practices/Language-Interop-and-.Net.md b/Best-Practices/Language-Interop-and-.Net.md index e5df50b..f0d697b 100644 --- a/Best-Practices/Language-Interop-and-.Net.md +++ b/Best-Practices/Language-Interop-and-.Net.md @@ -30,7 +30,7 @@ The `#requires` statement will prevent the script from running on the wrong vers ### PowerShell Supported Version -When working in an environment where there are multiple versions of PowerShell make sure to specify the lowest version your script will support by prividing a Requires statement at the top of the script. +When working in an environment where there are multiple versions of PowerShell make sure to specify the lowest version your script will support by providing a Requires statement at the top of the script. ```PowerShell #Requires -Version 2.0 From 7ad837213c11e6d3bfb30ab25be3c625a3d21666 Mon Sep 17 00:00:00 2001 From: Kenyon Ralph Date: Sat, 21 Jan 2023 21:02:23 -0800 Subject: [PATCH 074/113] Code-Layout-and-Formatting.md: grammar fix --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index a39790d..cdcacfa 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -94,7 +94,7 @@ Get-ChildItem | Where-Object { $_.Length -gt 10mb } The primary reason for this recommendation is practical: there are no exceptions necessary when following this rule, and when code is written following this style, _new lines_ of code can be inserted between any two lines with no risk of accidentally breaking the code by separating braces from their statement blocks. Thus, it's easier to follow, and makes errors less likely. -Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some addition reasoning here: First: in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second: PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. +Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some additional reasoning here: First: in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second: PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. #### Always Start With CmdletBinding From f19883de2abc4413bd4449b372ff566066d5eda3 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 03:26:28 +0100 Subject: [PATCH 075/113] Make .NET written consistently the same throughout --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index a39790d..b66b2b2 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -21,7 +21,7 @@ PowerShell is **not** case sensitive, but we follow capitalization conventions t * PascalCase - capitalize the first letter of each word * camelCase - capitalize the first letter of each word _except_ the first. -PowerShell uses PascalCase for _all_ public identifiers: module names, function or cmdlet names, class, enum, and attribute names, public fields or properties, global variables and constants, etc. In fact, since the _parameters_ to PowerShell commands are actually _properties_ of .Net classes, even parameters use PascalCase rather than camelCase. +PowerShell uses PascalCase for _all_ public identifiers: module names, function or cmdlet names, class, enum, and attribute names, public fields or properties, global variables and constants, etc. In fact, since the _parameters_ to PowerShell commands are actually _properties_ of .NET classes, even parameters use PascalCase rather than camelCase. PowerShell language keywords are written in lower case (yes, even `foreach` and `dynamicparam`), as well as operators such as `-eq` and `-match`. The keywords in comment-based help are written in UPPERCASE to make it easy to spot them among the dense prose of documentation. From 8555e2121934dcd1da435f2f6245c5cc17bde1bd Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 22:26:05 +0100 Subject: [PATCH 076/113] Change 'addition' to 'additional'. --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index b66b2b2..76aaa41 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -94,7 +94,7 @@ Get-ChildItem | Where-Object { $_.Length -gt 10mb } The primary reason for this recommendation is practical: there are no exceptions necessary when following this rule, and when code is written following this style, _new lines_ of code can be inserted between any two lines with no risk of accidentally breaking the code by separating braces from their statement blocks. Thus, it's easier to follow, and makes errors less likely. -Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some addition reasoning here: First: in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second: PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. +Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some additional reasoning here: First: in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second: PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. #### Always Start With CmdletBinding From b9dd96e37ba655634bd31ae989ec99fb457475c5 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 22:28:07 +0100 Subject: [PATCH 077/113] Change some inconsistent punctuation marks. --- Style-Guide/Code-Layout-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Code-Layout-and-Formatting.md b/Style-Guide/Code-Layout-and-Formatting.md index 76aaa41..84e5598 100644 --- a/Style-Guide/Code-Layout-and-Formatting.md +++ b/Style-Guide/Code-Layout-and-Formatting.md @@ -94,7 +94,7 @@ Get-ChildItem | Where-Object { $_.Length -gt 10mb } The primary reason for this recommendation is practical: there are no exceptions necessary when following this rule, and when code is written following this style, _new lines_ of code can be inserted between any two lines with no risk of accidentally breaking the code by separating braces from their statement blocks. Thus, it's easier to follow, and makes errors less likely. -Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some additional reasoning here: First: in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second: PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. +Because this choice was somewhat contentious in the community (about 1/3 of voters opposed), it's worth adding some additional reasoning here. First, in some historical consoles, it was necessary to write this way, so much of the early PowerShell code follows this style anyway. Second, PowerShell functions which accept scriptblocks (such as `ForEach-Object` and `Where-Object`) are common, and an _inherent_ part of the syntax of important PowerShell-based domain-specific languages such as DSC. Since it's **required** to place the opening brace on the end of the line in those cases, the only _consistent_ option is to follow OTBS. #### Always Start With CmdletBinding From 410532aac3b10b6a7eb315e649c42b1a745fe7c0 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 22:50:45 +0100 Subject: [PATCH 078/113] Change 'Windows PowerShell' to 'PowerShell'. As Windows is not the only OS PowerShell can run on, having 'Windows' in the name is not very practical anymore. That's why, starting with version 6, Microsoft decided to change 'Windows PowerShell' to 'PowerShell'. The text now follows this. --- Style-Guide/Function-Structure.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Style-Guide/Function-Structure.md b/Style-Guide/Function-Structure.md index 9af1e8b..ecef736 100644 --- a/Style-Guide/Function-Structure.md +++ b/Style-Guide/Function-Structure.md @@ -155,7 +155,7 @@ function Get-User { * **ValidateCount** Validation Attribute The ValidateCount attribute specifies the minimum and maximum number - of parameter values that a parameter accepts. Windows PowerShell + of parameter values that a parameter accepts. PowerShell generates an error if the number of parameter values in the command that calls the function is outside that range. @@ -171,7 +171,7 @@ function Get-User { * **ValidateLength** Validation Attribute The ValidateLength attribute specifies the minimum and maximum number - of characters in a parameter or variable value. Windows PowerShell generates an + of characters in a parameter or variable value. PowerShell generates an error if the length of a value specified for a parameter or a variable is outside of the range. @@ -187,7 +187,7 @@ function Get-User { * **ValidatePattern** Validation Attribute The ValidatePattern attribute specifies a regular expression that - is compared to the parameter or variable value. Windows PowerShell generates + is compared to the parameter or variable value. PowerShell generates an error if the value does not match the regular expression pattern. @@ -203,7 +203,7 @@ function Get-User { * **ValidateRange** Validation Attribute The ValidateRange attribute specifies a numeric range for each - parameter or variable value. Windows PowerShell generates an error + parameter or variable value. PowerShell generates an error if any value is outside that range. ```PowerShell @@ -218,7 +218,7 @@ function Get-User { * **ValidateScript** Validation Attribute The ValidateScript attribute specifies a script that is used - to validate a parameter or variable value. Windows PowerShell + to validate a parameter or variable value. PowerShell pipes the value to the script, and generates an error if the script returns "false" or if the script throws an exception. @@ -238,7 +238,7 @@ function Get-User { * **ValidateSet** Attribute The ValidateSet attribute specifies a set of valid values for a - parameter or variable. Windows PowerShell generates an error if a + parameter or variable. PowerShell generates an error if a parameter or variable value does not match a value in the set. In the following example, the value of the Detail parameter can only be "Low," "Average," or "High." @@ -255,7 +255,7 @@ function Get-User { * **ValidateNotNull** Validation Attribute The ValidateNotNull attribute specifies that the parameter - value cannot be null ($null). Windows PowerShell generates an + value cannot be null ($null). PowerShell generates an error if the parameter value is null. The ValidateNotNull attribute is designed to be used when the @@ -277,7 +277,7 @@ function Get-User { The ValidateNotNullOrEmpty attribute specifies that the parameter value cannot be null ($null) and cannot be an empty string (""). - Windows PowerShell generates an error if the parameter is used in + PowerShell generates an error if the parameter is used in a function call, but its value is null, an empty string, or an empty array. From 10e35e6a2ecd4ee5ae77236aae3b6af7b69efec0 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 22:53:26 +0100 Subject: [PATCH 079/113] Change 'Null' to 'null' to keep it consistent. --- Style-Guide/Function-Structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Function-Structure.md b/Style-Guide/Function-Structure.md index ecef736..87d9a3b 100644 --- a/Style-Guide/Function-Structure.md +++ b/Style-Guide/Function-Structure.md @@ -260,7 +260,7 @@ function Get-User { The ValidateNotNull attribute is designed to be used when the type of the parameter value is not specified or when the specified - type will accept a value of Null. (If you specify a type that will + type will accept a value of null. (If you specify a type that will not accept a null value, such as a string, the null value will be rejected without the ValidateNotNull attribute, because it does not match the specified type.) From 5f7cf65a211af3d25cb13c1e253c821bf19dd0d3 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 22:59:20 +0100 Subject: [PATCH 080/113] Change "it's" to "its" as it is used possessively. --- Style-Guide/Documentation-and-Comments.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index d0ad3e2..70342b6 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -73,7 +73,7 @@ If you want to provide detailed explanations about how your tool works, use the ##### Describe The Function -Every script function command should have at least a short statement describing it's function. That is the `Synopsis`. +Every script function command should have at least a short statement describing its function. That is the `Synopsis`. ##### Document Each Parameter From 59b660dfd5fc8fc002b0ee2b9c3c710632c81c58 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:03:50 +0100 Subject: [PATCH 081/113] Change 'Powershell' to 'PowerShell'. Two times 'Powershell' was written in this text instead of 'PowerShell', which makes them inconsistent with the rest of the text where it was properly written as 'PowerShell'. --- Style-Guide/Documentation-and-Comments.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Style-Guide/Documentation-and-Comments.md b/Style-Guide/Documentation-and-Comments.md index 70342b6..af79fe0 100644 --- a/Style-Guide/Documentation-and-Comments.md +++ b/Style-Guide/Documentation-and-Comments.md @@ -98,7 +98,7 @@ It is also possible to write `.PARAMETER` statements with the rest of the docume ##### Provide Usage Examples -Your help should always provide an example for each major use case. A 'usage example' is just an example of what you would type in to Powershell to run the script - you can even cut and paste one from the command line while you're testing your function. +Your help should always provide an example for each major use case. A 'usage example' is just an example of what you would type in to PowerShell to run the script - you can even cut and paste one from the command line while you're testing your function. ```PowerShell function Test-Help { @@ -171,4 +171,4 @@ Comment-based help is displayed when the user types `help Get-Example` or `Get-E Your help should be helpful. That is, if you've written a tool called `Get-LOBAppUser`, don't write help that merely says, "Gets LOB App Users." Duh. -**Further information:** You can get more on the use of comment-based help by typing `help about_Comment_Based_Help` within Powershell. +**Further information:** You can get more on the use of comment-based help by typing `help about_Comment_Based_Help` within PowerShell. From 7539a35b0d111966907d50fe82ccb1d7318445dd Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:11:57 +0100 Subject: [PATCH 082/113] Change '.Net' to '.NET'. The proper name of '.NET' is '.NET', all capitals, not '.Net'. --- Style-Guide/Naming-Conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index b65a14f..05cf924 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -28,7 +28,7 @@ Get-Process -Name Explorer #### Use full, explicit paths when possible. -When writing scripts, it is only safe to use `..` or `.` in a path if you have previously set the location explicitly (within the current function or script). Even if you _have_ explictly set the path, you must beware of using relative paths when calling .Net methods or legacy/native applications, because they will use `[Environment]::CurrentDirectory` which is not automatically updated to PowerShell's present working directory (`$PWD`). +When writing scripts, it is only safe to use `..` or `.` in a path if you have previously set the location explicitly (within the current function or script). Even if you _have_ explictly set the path, you must beware of using relative paths when calling .NET methods or legacy/native applications, because they will use `[Environment]::CurrentDirectory` which is not automatically updated to PowerShell's present working directory (`$PWD`). Because troubleshooting these types of errors is tedious (and they are easy to over-look) it's best to avoid using relative paths altogether, and instead, base your paths off of $PSScriptRoot (the folder your script is in) when necessary. From ecd84a1c8359d671bd8065e8b9ac645959de08f6 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:12:53 +0100 Subject: [PATCH 083/113] Add missing comma to make the sentence correct. --- Style-Guide/Naming-Conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index 05cf924..d6314b8 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -30,7 +30,7 @@ Get-Process -Name Explorer When writing scripts, it is only safe to use `..` or `.` in a path if you have previously set the location explicitly (within the current function or script). Even if you _have_ explictly set the path, you must beware of using relative paths when calling .NET methods or legacy/native applications, because they will use `[Environment]::CurrentDirectory` which is not automatically updated to PowerShell's present working directory (`$PWD`). -Because troubleshooting these types of errors is tedious (and they are easy to over-look) it's best to avoid using relative paths altogether, and instead, base your paths off of $PSScriptRoot (the folder your script is in) when necessary. +Because troubleshooting these types of errors is tedious (and they are easy to over-look), it's best to avoid using relative paths altogether, and instead, base your paths off of $PSScriptRoot (the folder your script is in) when necessary. ```PowerShell # Do not write: From c7defc81fffbe1ec72b2b6528dc13b1cc81493a6 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:13:58 +0100 Subject: [PATCH 084/113] Change '.net' to '.NET'. --- Style-Guide/Naming-Conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index d6314b8..5a52224 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -49,7 +49,7 @@ Get-Content -Path (Join-Path -Path $PSScriptRoot -ChildPath README.md) # Or to use string concatenation: Get-Content "$PSScriptRoot\README.md" -# For calling .net methods, pass full paths: +# For calling .NET methods, pass full paths: [System.IO.File]::ReadAllText("$PSScriptRoot\README.md") # Optionally by calling Convert-Path From 04bcaf2e676ffcb7caf83979308a9944bec56ed9 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:15:25 +0100 Subject: [PATCH 085/113] Change '...' to '.'. --- Style-Guide/Naming-Conventions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Naming-Conventions.md b/Style-Guide/Naming-Conventions.md index 5a52224..0035315 100644 --- a/Style-Guide/Naming-Conventions.md +++ b/Style-Guide/Naming-Conventions.md @@ -60,7 +60,7 @@ Push-Location $PSScriptRoot ##### Avoid the use of `~` to represent the home folder. -The meaning of ~ is unfortunately dependent on the "current" provider at the time of execution. This isn't really a style issue, but it's an important rule for code you intend to share anyway. Instead, use `${Env:UserProfile}` or `(Get-PSProvider -PSProvider FileSystem).Home` ... +The meaning of ~ is unfortunately dependent on the "current" provider at the time of execution. This isn't really a style issue, but it's an important rule for code you intend to share anyway. Instead, use `${Env:UserProfile}` or `(Get-PSProvider -PSProvider FileSystem).Home`. ```PowerShell PS C:\Windows\system32> cd ~ From 454dfe464ecfb2d43f6e5a3d3e741c018ad89e35 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:32:06 +0100 Subject: [PATCH 086/113] Change 'when' to 'which'. --- Best-Practices/Building-Reusable-Tools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Best-Practices/Building-Reusable-Tools.md b/Best-Practices/Building-Reusable-Tools.md index 964fe7f..9f0774c 100644 --- a/Best-Practices/Building-Reusable-Tools.md +++ b/Best-Practices/Building-Reusable-Tools.md @@ -2,7 +2,7 @@ For this discussion, it's important to have some agreed-upon terminology. While the terminology here isn't used universally, the community generally agrees that several types of "script" exist: -1. Some scripts contain tools, when are meant to be reusable. These are typically functions or advanced functions, and they are typically contained in a script module or in a function library of some kind. These tools are designed for a high level of re-use. +1. Some scripts contain tools, which are meant to be reusable. These are typically functions or advanced functions, and they are typically contained in a script module or in a function library of some kind. These tools are designed for a high level of re-use. 2. Some scripts are controllers, meaning they are intended to utilize one or more tools (functions, commands, etc) to automate a specific business process. A script is not intended to be reusable; it is intended to make use of reuse by leveraging functions and other commands For example, you might write a "New-CorpUser" script, which provisions new users. In it, you might call numerous commands and functions to create a user account, mailbox-enable them, provision a home folder, and so on. Those discrete tasks might also be used in other processes, so you build them as functions. The script is only intended to automate that one process, and so it doesn't need to exhibit reusability concepts. It's a standalone thing. From 59ac64665085a354e32bd3f47d5c15c79e61f7d9 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:38:28 +0100 Subject: [PATCH 087/113] Change non-existent words. Change 're-use' to 'reuse' and 're-used' to 'reused', as these words are written as such in both Merriam-Webster and the Cambridge Dictionary. Furthermore, in another location in the text the word was already written as 'reuse' and 'reusable', so it was also inconsistent. --- Best-Practices/Building-Reusable-Tools.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Best-Practices/Building-Reusable-Tools.md b/Best-Practices/Building-Reusable-Tools.md index 9f0774c..7729cba 100644 --- a/Best-Practices/Building-Reusable-Tools.md +++ b/Best-Practices/Building-Reusable-Tools.md @@ -2,7 +2,7 @@ For this discussion, it's important to have some agreed-upon terminology. While the terminology here isn't used universally, the community generally agrees that several types of "script" exist: -1. Some scripts contain tools, which are meant to be reusable. These are typically functions or advanced functions, and they are typically contained in a script module or in a function library of some kind. These tools are designed for a high level of re-use. +1. Some scripts contain tools, which are meant to be reusable. These are typically functions or advanced functions, and they are typically contained in a script module or in a function library of some kind. These tools are designed for a high level of reuse. 2. Some scripts are controllers, meaning they are intended to utilize one or more tools (functions, commands, etc) to automate a specific business process. A script is not intended to be reusable; it is intended to make use of reuse by leveraging functions and other commands For example, you might write a "New-CorpUser" script, which provisions new users. In it, you might call numerous commands and functions to create a user account, mailbox-enable them, provision a home folder, and so on. Those discrete tasks might also be used in other processes, so you build them as functions. The script is only intended to automate that one process, and so it doesn't need to exhibit reusability concepts. It's a standalone thing. @@ -14,7 +14,7 @@ Controllers, on the other hand, often produce output directly to the screen (whe Generally, people tend to feel that most working code - that is, your code which does things - should be modularized into functions and ideally stored in script modules. -That makes those functions more easily re-used. Those functions should exhibit a high level of reusability, such as accepting input only via parameters and producing output only as objects to the pipeline +That makes those functions more easily reused. Those functions should exhibit a high level of reusability, such as accepting input only via parameters and producing output only as objects to the pipeline # TOOL-03 Make tools as re-usable as possible From fbb9f5543c3bd7323a15d9785d49b1a5ee2ef08e Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:39:06 +0100 Subject: [PATCH 088/113] Add dot to the end of a sentence. --- Best-Practices/Building-Reusable-Tools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Best-Practices/Building-Reusable-Tools.md b/Best-Practices/Building-Reusable-Tools.md index 7729cba..a07ae38 100644 --- a/Best-Practices/Building-Reusable-Tools.md +++ b/Best-Practices/Building-Reusable-Tools.md @@ -3,7 +3,7 @@ For this discussion, it's important to have some agreed-upon terminology. While the terminology here isn't used universally, the community generally agrees that several types of "script" exist: 1. Some scripts contain tools, which are meant to be reusable. These are typically functions or advanced functions, and they are typically contained in a script module or in a function library of some kind. These tools are designed for a high level of reuse. -2. Some scripts are controllers, meaning they are intended to utilize one or more tools (functions, commands, etc) to automate a specific business process. A script is not intended to be reusable; it is intended to make use of reuse by leveraging functions and other commands +2. Some scripts are controllers, meaning they are intended to utilize one or more tools (functions, commands, etc) to automate a specific business process. A script is not intended to be reusable; it is intended to make use of reuse by leveraging functions and other commands. For example, you might write a "New-CorpUser" script, which provisions new users. In it, you might call numerous commands and functions to create a user account, mailbox-enable them, provision a home folder, and so on. Those discrete tasks might also be used in other processes, so you build them as functions. The script is only intended to automate that one process, and so it doesn't need to exhibit reusability concepts. It's a standalone thing. From f0dc492567df645799dab822e42337c9ebe188f1 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:45:27 +0100 Subject: [PATCH 089/113] Add 'controller'. In the explanation about controller scripts, it read 'A script is not intended to be reusable', which made no sense to me, so I changed it to 'A controller script is not intended to be reusable'. --- Best-Practices/Building-Reusable-Tools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Best-Practices/Building-Reusable-Tools.md b/Best-Practices/Building-Reusable-Tools.md index a07ae38..833a77a 100644 --- a/Best-Practices/Building-Reusable-Tools.md +++ b/Best-Practices/Building-Reusable-Tools.md @@ -3,7 +3,7 @@ For this discussion, it's important to have some agreed-upon terminology. While the terminology here isn't used universally, the community generally agrees that several types of "script" exist: 1. Some scripts contain tools, which are meant to be reusable. These are typically functions or advanced functions, and they are typically contained in a script module or in a function library of some kind. These tools are designed for a high level of reuse. -2. Some scripts are controllers, meaning they are intended to utilize one or more tools (functions, commands, etc) to automate a specific business process. A script is not intended to be reusable; it is intended to make use of reuse by leveraging functions and other commands. +2. Some scripts are controllers, meaning they are intended to utilize one or more tools (functions, commands, etc) to automate a specific business process. A controller script is not intended to be reusable; it is intended to make use of reuse by leveraging functions and other commands. For example, you might write a "New-CorpUser" script, which provisions new users. In it, you might call numerous commands and functions to create a user account, mailbox-enable them, provision a home folder, and so on. Those discrete tasks might also be used in other processes, so you build them as functions. The script is only intended to automate that one process, and so it doesn't need to exhibit reusability concepts. It's a standalone thing. From 58e9ab6fe75033f3b5ca1d84b7a3f42702181374 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:52:54 +0100 Subject: [PATCH 090/113] Delete 'on the other hand'. In the example paragraph under TOOL-01, it is clear a controller script is being explained. In the paragraph following that, a further remark is made about controller scripts, but it read 'Controllers, on the other hand, (...)', which seems to imply the paragraph before that was talking about a tool script, which wasn't the case (it was talking about a controller script helped by several tool scripts, so tool scripts were also mentioned, but the paragraph ends with two sentences about the controller script, so 'on the other hand' in the next paragraph is incorrect and confusing IMHO). --- Best-Practices/Building-Reusable-Tools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Best-Practices/Building-Reusable-Tools.md b/Best-Practices/Building-Reusable-Tools.md index 833a77a..963d667 100644 --- a/Best-Practices/Building-Reusable-Tools.md +++ b/Best-Practices/Building-Reusable-Tools.md @@ -7,7 +7,7 @@ For this discussion, it's important to have some agreed-upon terminology. While For example, you might write a "New-CorpUser" script, which provisions new users. In it, you might call numerous commands and functions to create a user account, mailbox-enable them, provision a home folder, and so on. Those discrete tasks might also be used in other processes, so you build them as functions. The script is only intended to automate that one process, and so it doesn't need to exhibit reusability concepts. It's a standalone thing. -Controllers, on the other hand, often produce output directly to the screen (when designed for interactive use), or may log to a file (when designed to run unattended). +Controllers often produce output directly to the screen (when designed for interactive use), or may log to a file (when designed to run unattended). # TOOL-02 Make your code modular From 2e1d9360778747ddae0c8dcd113fbe1f23094798 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:54:55 +0100 Subject: [PATCH 091/113] Delete inconsistent line spacing. Some paragraphs were separated by one empty line, some by two. The second empty line at several places has been deleted. --- Best-Practices/Building-Reusable-Tools.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Best-Practices/Building-Reusable-Tools.md b/Best-Practices/Building-Reusable-Tools.md index 963d667..5642d8f 100644 --- a/Best-Practices/Building-Reusable-Tools.md +++ b/Best-Practices/Building-Reusable-Tools.md @@ -9,14 +9,12 @@ For example, you might write a "New-CorpUser" script, which provisions new users Controllers often produce output directly to the screen (when designed for interactive use), or may log to a file (when designed to run unattended). - # TOOL-02 Make your code modular Generally, people tend to feel that most working code - that is, your code which does things - should be modularized into functions and ideally stored in script modules. That makes those functions more easily reused. Those functions should exhibit a high level of reusability, such as accepting input only via parameters and producing output only as objects to the pipeline - # TOOL-03 Make tools as re-usable as possible Tools should accept input from parameters and should (in most cases) produce any output to the pipeline; this approach helps maximize reusability. @@ -45,7 +43,6 @@ For example, a function named Get-DiskInfo would return disk sizing information An intermediate step is useful for tools that are packaged in script modules: views. By building a manifest for the module, you can have the module also include a custom .format.ps1xml view definition file. The view can specify manipulated data values, such as the default view used by PowerShell to display the output of Get-Process. The view does not manipulate the underlying data, leaving the raw data available for any purpose. - # WAST-01 Don't re-invent the wheel There are a number of approaches in PowerShell that will "get the job done." In some cases, other community members may have already written the code to achieve your objectives. If that code meets your needs, then you might save yourself some time by leveraging it, instead of writing it yourself. @@ -77,15 +74,12 @@ It has been argued by some that, "I didn't know such-and-such existed, so I wrot On the flip side, it's important to note that writing your own code from the ground up can be useful if you are trying to learn a particular concept, or if you have specific needs that are not offered by another existing solution. - # WAST-02 Report bugs to Microsoft An exception: if you know that a built-in technique doesn't work properly (e.g., it is buggy or doesn't accomplish the exact task), then obviously it's fine to re-invent the wheel. However, in cases where you're doing so to avoid a bug or design flaw, then you should - as an upstanding member of the community - report the bug on [github.com/powershell](https://github.com/PowerShell/PowerShell/issues) also. - TODO: The "PURE" section is dubious at best. We need to discuss it. - # PURE-01 Use native PowerShell where possible This means not using COM, .NET Framework classes, and so on when there is a native Windows PowerShell command or technique that gets the job done. @@ -103,4 +97,3 @@ Document the reason for using tools other than PowerShell in your comments. That said, you truly become a better PowerShell person if you take the time to wrap a less-preferred way in an advanced function or cmdlet. Then you get the best of both worlds: the ability to reach outside the shell itself for functionality, while keeping the advantages of native commands. Ignorance, however, is no excuse. If you've written some big wrapper function around Ping.exe simply because you were unaware of Test-Connection, then you've wasted a lot of time, and that is not commendable. Before you move on to a less-preferred approach, make sure the shell doesn't already have a way to do what you're after. - From a28f24f50e26e18c7d226710ae8856451601c466 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sat, 28 Jan 2023 23:57:00 +0100 Subject: [PATCH 092/113] Add dot to sentences. At two locations, a dot was missing at the end of the sentence. This has been corrected. --- Best-Practices/Building-Reusable-Tools.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Best-Practices/Building-Reusable-Tools.md b/Best-Practices/Building-Reusable-Tools.md index 5642d8f..227791e 100644 --- a/Best-Practices/Building-Reusable-Tools.md +++ b/Best-Practices/Building-Reusable-Tools.md @@ -13,7 +13,7 @@ Controllers often produce output directly to the screen (when designed for inter Generally, people tend to feel that most working code - that is, your code which does things - should be modularized into functions and ideally stored in script modules. -That makes those functions more easily reused. Those functions should exhibit a high level of reusability, such as accepting input only via parameters and producing output only as objects to the pipeline +That makes those functions more easily reused. Those functions should exhibit a high level of reusability, such as accepting input only via parameters and producing output only as objects to the pipeline. # TOOL-03 Make tools as re-usable as possible @@ -29,7 +29,7 @@ You can get a list of the verbs by typing 'get-verb' at the command line. Tools should be consistent with PowerShell native cmdlets in regards parameter naming. -For example, use $ComputerName and $ServerInstance rather than something like $Param_Computer or $InstanceName +For example, use $ComputerName and $ServerInstance rather than something like $Param_Computer or $InstanceName. # TOOL-06 Tools should output raw data From fa28ceaed07a027eb5301b45d14b1ecde52b7ebd Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 29 Jan 2023 00:00:27 +0100 Subject: [PATCH 093/113] Correct 'in regards'. In the text it was written '(..) in regards parameter naming'. This is incorrect, according to both Merriam-Webster and the Cambridge Dictionary: it is 'in regard to'. This has been corrected. --- Best-Practices/Building-Reusable-Tools.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Best-Practices/Building-Reusable-Tools.md b/Best-Practices/Building-Reusable-Tools.md index 227791e..47c5fa9 100644 --- a/Best-Practices/Building-Reusable-Tools.md +++ b/Best-Practices/Building-Reusable-Tools.md @@ -27,7 +27,7 @@ You can get a list of the verbs by typing 'get-verb' at the command line. # TOOL-05 Use PowerShell standard parameter naming -Tools should be consistent with PowerShell native cmdlets in regards parameter naming. +Tools should be consistent with PowerShell native cmdlets in regard to parameter naming. For example, use $ComputerName and $ServerInstance rather than something like $Param_Computer or $InstanceName. From dfb06f4f55b21f1d133ce15987368bb005306361 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 29 Jan 2023 03:09:41 +0100 Subject: [PATCH 094/113] Write 'pipeline' consistently throughout. At one location 'Pipeline' was written and everywhere else 'pipeline', which is inconsistent, so 'Pipeline' has been changed to 'pipeline'. --- Style-Guide/Function-Structure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Style-Guide/Function-Structure.md b/Style-Guide/Function-Structure.md index 87d9a3b..046412e 100644 --- a/Style-Guide/Function-Structure.md +++ b/Style-Guide/Function-Structure.md @@ -80,7 +80,7 @@ function Get-USCitizenCapability { #### Always use CmdletBinding attribute. -#### Always have at least a `process {}` code block if any parameters takes values from the Pipeline. +#### Always have at least a `process {}` code block if any parameters takes values from the pipeline. #### Specify an OutputType attribute if the advanced function returns an object or collection of objects. From 845f55ef87dca1c8d57de0b2f9ef0f5ec402f9cd Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 16 Apr 2023 10:45:14 +0200 Subject: [PATCH 095/113] Change few typos Changed some typos like 'a interactions' and the use of 'i.e.' (misspelled as 'ie'), which means 'that is', while 'e.g.' seemed to be the correct abbreviation in that context, which means 'for example'. --- Best-Practices/Output-and-Formatting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Best-Practices/Output-and-Formatting.md b/Best-Practices/Output-and-Formatting.md index 702fa5a..27a84a6 100644 --- a/Best-Practices/Output-and-Formatting.md +++ b/Best-Practices/Output-and-Formatting.md @@ -6,7 +6,7 @@ TODO: This whole document is STILL ROUGH DRAFT Previous to PowerShell 5, Write-Host has no functionality at all in non-interactive scripts. It cannot be captured or redirected, and therefore should only be used in functions which are "Show"ing or "Format"ing output, or to display something as part of an interactive prompt to the user. -That is: you should not use Write-Host to create script output unless your script (or function, or whatever) uses the Show verb (as in, Show-Performance) or the Format verb (as in, Format-Hex), or has a `-Formatted` switch parameter. You may also use it to build a interactions with the user in other cases (e.g. to write extra information to the screen before prompting the user for a choice or input). +That is: you should not use Write-Host to create script output unless your script (or function, or whatever) uses the Show verb (as in, Show-Performance) or the Format verb (as in, Format-Hex), or has a `-Formatted` switch parameter. You may also use it to build interactions with the user in other cases (e.g. to write extra information to the screen before prompting the user for a choice or input). Generally, you should consider the other Write-* commands first when trying to give information to the user. @@ -22,7 +22,7 @@ You should use verbose output for information that contains details about the va ## Use Write-Debug to give information to someone maintaining your script -You should use the debug output stream for output that is useful for script debugging (ie: "Now entering main loop" or "Result was null, skipping to end of loop"), or to display the value of a variable before a conditional statement, so the maintainer can break into the debugger if necessary. +You should use the debug output stream for output that is useful for script debugging (e.g.: "Now entering main loop" or "Result was null, skipping to end of loop"), or to display the value of a variable before a conditional statement, so the maintainer can break into the debugger if necessary. > TIP: When debugging you should be aware that you can set `$DebugPreference = "Continue"` to see this information on screen without entering a breakpoint prompt. From 4ef6c3158439e9b48858ab16b8493573ff7ffee5 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 16 Apr 2023 10:46:36 +0200 Subject: [PATCH 096/113] Update Output-and-Formatting.md --- Best-Practices/Output-and-Formatting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Best-Practices/Output-and-Formatting.md b/Best-Practices/Output-and-Formatting.md index 27a84a6..10798cf 100644 --- a/Best-Practices/Output-and-Formatting.md +++ b/Best-Practices/Output-and-Formatting.md @@ -6,7 +6,7 @@ TODO: This whole document is STILL ROUGH DRAFT Previous to PowerShell 5, Write-Host has no functionality at all in non-interactive scripts. It cannot be captured or redirected, and therefore should only be used in functions which are "Show"ing or "Format"ing output, or to display something as part of an interactive prompt to the user. -That is: you should not use Write-Host to create script output unless your script (or function, or whatever) uses the Show verb (as in, Show-Performance) or the Format verb (as in, Format-Hex), or has a `-Formatted` switch parameter. You may also use it to build interactions with the user in other cases (e.g. to write extra information to the screen before prompting the user for a choice or input). +That is: you should not use Write-Host to create script output unless your script (or function, or whatever) uses the Show verb (as in, Show-Performance) or the Format verb (as in, Format-Hex), or has a `-Formatted` switch parameter. You may also use it to build interactions with the user in other cases (e.g., to write extra information to the screen before prompting the user for a choice or input). Generally, you should consider the other Write-* commands first when trying to give information to the user. @@ -22,7 +22,7 @@ You should use verbose output for information that contains details about the va ## Use Write-Debug to give information to someone maintaining your script -You should use the debug output stream for output that is useful for script debugging (e.g.: "Now entering main loop" or "Result was null, skipping to end of loop"), or to display the value of a variable before a conditional statement, so the maintainer can break into the debugger if necessary. +You should use the debug output stream for output that is useful for script debugging (e.g., "Now entering main loop" or "Result was null, skipping to end of loop"), or to display the value of a variable before a conditional statement, so the maintainer can break into the debugger if necessary. > TIP: When debugging you should be aware that you can set `$DebugPreference = "Continue"` to see this information on screen without entering a breakpoint prompt. From d82198f3a0dc37264a7576cd4446467c2d1d5730 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 16 Apr 2023 10:48:15 +0200 Subject: [PATCH 097/113] Update Output-and-Formatting.md --- Best-Practices/Output-and-Formatting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Best-Practices/Output-and-Formatting.md b/Best-Practices/Output-and-Formatting.md index 10798cf..d1a7fc0 100644 --- a/Best-Practices/Output-and-Formatting.md +++ b/Best-Practices/Output-and-Formatting.md @@ -48,7 +48,7 @@ When you combine the output of multiple types objects, they should generally be ### Two important exceptions to the single-type rule -**For internal functions** it's ok to return multiple different types because they won't be "output" to the user/host, and can offer significant savings (e.g. one database call with three table joins, instead of three database calls with two or three joins each). You can then call these functions and assign the output to multiple variables, like so: +**For internal functions** it's ok to return multiple different types because they won't be "output" to the user/host, and can offer significant savings (e.g., one database call with three table joins, instead of three database calls with two or three joins each). You can then call these functions and assign the output to multiple variables, like so: ```PowerShell $user, $group, $org = Get-UserGroupOrg From 670a534612fa46b10e25978cd1cd85bf366cdbf5 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 16 Apr 2023 11:08:16 +0200 Subject: [PATCH 098/113] Update Performance.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add space between 'foreach' and the following parenthesis. - Change the faulty 'a la' to the correct 'à la'. - Write '.NET' and '.NET Framework' consistently the same throughout. --- Best-Practices/Performance.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Best-Practices/Performance.md b/Best-Practices/Performance.md index 8657cab..cc2ae91 100644 --- a/Best-Practices/Performance.md +++ b/Best-Practices/Performance.md @@ -7,7 +7,7 @@ If you're aware of multiple techniques to accomplish something, and you're writi For example: ```PowerShell -foreach($result in Do-Something) { $result.PropertyOne + $result.PropertyTwo } +foreach ($result in Do-Something) { $result.PropertyOne + $result.PropertyTwo } Do-Something | ForEach-Object { $_.PropertyOne + $_.PropertyTwo } ``` @@ -40,7 +40,7 @@ ForEach-Object -Process { } ``` -As described elsewhere in this guide, many folks in the community would dislike this approach for aesthetic reasons. However, this approach has the advantage of utilizing PowerShell's pipeline to "stream" the content in file.txt. Provided that the fictional "Do-Something" command isn't blocking the pipeline (a la Sort-Object), the shell can send lines of content (String objects, technically) through the pipeline in a continuous stream, rather than having to buffer them all into memory. +As described elsewhere in this guide, many folks in the community would dislike this approach for aesthetic reasons. However, this approach has the advantage of utilizing PowerShell's pipeline to "stream" the content in file.txt. Provided that the fictional "Do-Something" command isn't blocking the pipeline (à la Sort-Object), the shell can send lines of content (String objects, technically) through the pipeline in a continuous stream, rather than having to buffer them all into memory. Some would argue that this second approach is always a poor one, and that if performance is an issue then you should devolve from a PowerShell-native approach into a lower-level .NET Framework approach: @@ -73,8 +73,8 @@ The moral here is that both aesthetic and performance are important consideratio This is just a rough guideline, but as a general rule: -1. Language features are faster than features of the .net framework -2. Compiled methods on objects and .net classes are still faster than script +1. Language features are faster than features of the .NET Framework +2. Compiled methods on objects and .NET classes are still faster than script 3. Simple PowerShell script is still faster than calling functions or cmdlets It's counter-intuitive that script is faster than calling cmdlets that are compiled, but it's frequently true, unless there is a lot of work being done by each cmdlet. The overhead of calling cmdlets and passing data around is significant. Of course, this is just a guideline, and you should always **measure**. From 947784215347ed7dd33ddca45a0f6e86b342d067 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 16 Apr 2023 11:09:10 +0200 Subject: [PATCH 099/113] Update Performance.md Delete extra line. --- Best-Practices/Performance.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Best-Practices/Performance.md b/Best-Practices/Performance.md index cc2ae91..0b0a50e 100644 --- a/Best-Practices/Performance.md +++ b/Best-Practices/Performance.md @@ -78,4 +78,3 @@ This is just a rough guideline, but as a general rule: 3. Simple PowerShell script is still faster than calling functions or cmdlets It's counter-intuitive that script is faster than calling cmdlets that are compiled, but it's frequently true, unless there is a lot of work being done by each cmdlet. The overhead of calling cmdlets and passing data around is significant. Of course, this is just a guideline, and you should always **measure**. - From e06936a06313304d2c4bd78b54f67af57d1edae1 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 16 Apr 2023 11:19:07 +0200 Subject: [PATCH 100/113] Update Security.md - Change '.Net' to '.NET' to keep it consistent. - Change incorrect usage of spaces within example code (space after opening parenthesis and space before closing parenthesis). - Change several inconsistencies in example code: - Change '[System.Runtime.InteropServices.marshal]' to '[System.Runtime.InteropServices.Marshal]'. - Remove semicolon at the end of the lines. - Remove an unnecessary 'return' keyword. - Remove extra line at the end. --- Best-Practices/Security.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Best-Practices/Security.md b/Best-Practices/Security.md index e70f0ce..50c4472 100644 --- a/Best-Practices/Security.md +++ b/Best-Practices/Security.md @@ -16,11 +16,11 @@ param ( ) ``` -If you absolutely must pass a password in a plain string to a .Net API call or a third party library it is better to decrypt the credential as it is being passed instead of saving it in a variable. +If you absolutely must pass a password in a plain string to a .NET API call or a third party library, it is better to decrypt the credential as it is being passed instead of saving it in a variable. ```PowerShell # Get the cleartext password for a method call: - $Insecure.SetPassword( $Credentials.GetNetworkCredential().Password ) + $Insecure.SetPassword($Credentials.GetNetworkCredential().Password) ``` #### Other Secure Strings @@ -32,10 +32,10 @@ Note, if you ever need to turn a SecureString into a string, you can use this me ```PowerShell # Decrypt a secure string. - $BSTR = [System.Runtime.InteropServices.marshal]::SecureStringToBSTR($this); - $plaintext = [System.Runtime.InteropServices.marshal]::PtrToStringAuto($BSTR); - [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR); - return $plaintext + $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($this) + $plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) + [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR) + $plaintext ``` * For credentials that need to be saved to disk, serialize the credential object using @@ -63,4 +63,3 @@ computer where it was generated. # Read the Standard String from disk and convert to a SecureString $Secure = Get-Content -Path "${Env:AppData}\Sec.bin" | ConvertTo-SecureString ``` - From 6fbe21fe14b8d7faf60a61e20e2c1b8cb8cd013e Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 16 Apr 2023 12:23:25 +0200 Subject: [PATCH 101/113] Update TODO.md - Change '.Net' to '.NET' to keep it consistent. - Remove several spurious blank lines. - Change 'PowerShell Classes' to 'PowerShell classes'. - Change "'s" to "or". - Change 'makes sense more sense' to 'makes more sense'. - Move 'param()' in '[CmdletBinding()]param()' to the next line. - Change 'comment based help' to 'comment-based help'. - Change 'a about_ModuleName' to 'an about_ModuleName'. - Change 'that pases parameters positionally' to 'that passes parameters positionally'. - Change 'PsCmdlet.ThrowTerminatingError' to '$PSCmdlet.ThrowTerminatingError'. - Change 'PsCmdlet.WriteError' to '$PSCmdlet.WriteError'. - Change inconsistencies with the styling guide/conventions in the example code for function ThrowError: - Change typo 'errorrecord' to 'error record'. - Move brace after 'function ThrowError' to the same line. - Move parenthesis after 'param' to the same line. - Change 'parameter' to 'Parameter'. - Change '$exception = New-Object $ExceptionName $ExceptionMessage;' to '$exception = New-Object -TypeName $ExceptionName -ArgumentList $ExceptionMessage' (so using named parameters instead of positional parameters and removing the unnecessary semicolon at the end). - Change 'Discuss: when is this critical (-whatif) and optional (-confirm_' to 'Discuss: when is this critical (-WhatIf) and optional (-Confirm)'. - Change 'Discuss: when should you call PSCmdlet.ShouldProcess vs PSCmdlet.ShouldContinue (-Force)' to 'Discuss: when should you call $PSCmdlet.ShouldProcess vs $PSCmdlet.ShouldContinue (-Force)'. - Change more inconsistencies in example code: - Remove unnecessary semicolons at the end of the line. - Add space after 'foreach'. - Remove inconsistent spacing. - Add space after 'if'. - Change more small typos. P.S.: Make '$errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject' code that actually works! This code will generate the error **New-Object: Cannot bind argument to parameter 'TypeName' because it is null.**! --- Best-Practices/TODO.md | 104 +++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 60 deletions(-) diff --git a/Best-Practices/TODO.md b/Best-Practices/TODO.md index 66e0641..2fe8cd6 100644 --- a/Best-Practices/TODO.md +++ b/Best-Practices/TODO.md @@ -1,6 +1,6 @@ These documents are in an extremely rough state, not suitable for inclusion in the main guide yet. -### Using The .Net Framework +### Using the .NET Framework - [Control what gets exported in a module](#control-what-gets-exported-in-a-module) - [Specify when to use a Manifest for a module](#specify-when-to-use-a-manifest-for-a-module) @@ -51,19 +51,17 @@ TODO #### Control what gets exported in a module #### Specify when to use a Manifest for a module - #### Use RequiredAssemblies rather than Add-Type #### Use Add-Type rather than Reflection Avoid using `[System.Reflection]` to load assemblies when possible. Particularly avoid `LoadWithPartialName` (specify the full name instead). #### Use Add-Type for small classes or PInvoke calls -TODO: Is this better than PowerShell Classes, for compatibility? +TODO: Is this better than PowerShell classes, for compatibility? #### Prefer shipping binaries over large compilations -With PowerShell 5, security is tighter, and compiling code in memory will be frowned upon. Now that we have PowerShellGet and the PowerShell Gallery, there are few reasons 's no reason to avoid binaries. - -TODO: Discuss: when does embedding C# code makes sense more sense than just compiling it every time? +With PowerShell 5, security is tighter, and compiling code in memory will be frowned upon. Now that we have PowerShellGet and the PowerShell Gallery, there are few reasons or no reason to avoid binaries. +TODO: Discuss: when does embedding C# code makes more sense than just compiling it every time? ### Performance @@ -72,10 +70,10 @@ Prefer foreach(){} over ForEach-Object Prefer .foreach and .where over cmdlets Prefer functions with process blocks over ForEach-Object -#### Know when to use .Net -Prefer writing wrapper functions to just calling .Net APIs +#### Know when to use .NET +Prefer writing wrapper functions to just calling .NET APIs Discuss: System.IO file loading vs Get-Content (large files) -Discuss: Other examples of .Net API calls that are clearly faster? +Discuss: Other examples of .NET API calls that are clearly faster? Discuss: Casting for creating objects ### Error Handling @@ -85,7 +83,6 @@ Discuss: Avoid depending on `$?` -- why? Discuss: Never use `$Error` in scripts (always use -ErrorVariable) Discuss: Interactively, always copy $Error[0] to $e or something - ### General Design Principles #### Use custom objects @@ -103,26 +100,24 @@ Discuss: During development, always write scripts, which are automatically re-pa This is in the Style Guide too, but we should discuss it in more depth here, and link to it from the Style Guide. Scripts should start life as something like this: ``` -[CmdletBinding()]param() +[CmdletBinding()] +param() process{} end{} ``` You can always ignore one of the blocks, and add parameters and such, but you should never write a script without CmdletBinding, and you should never write one without at least considering making it take pipeline input - ### Include Help TODO: Link to StyleGuide about formatting help -Discuss: Minimum acceptable comment based help: Synopsis, Parameters, and an example for each parameter set (plus pipeline examples if you can contrive one) +Discuss: Minimum acceptable comment-based help: Synopsis, Parameters, and an example for each parameter set (plus pipeline examples if you can contrive one) Discuss: Benefits of MAML help files -#### Always ship a about_ModuleName with modules +#### Always ship an about_ModuleName with modules Discuss: Other reasons to write about_topics - - #### Prefer PipelineByPropertyName parameters. Discuss: This allows the most flexibility: piping objects and using scriptblocks to shape it for parameters. Unless you absolutely need to write a `begin` block and use this parameter in it, you probably should accept it on the pipeline. @@ -137,7 +132,7 @@ You can use aliases to map parameters to property names of objects which might b Particularly when splatting PSBoundParameters to the next function, if that function isn't `[CmdletBinding()]` (it should be!) you must remember to strip the common parameters if they're present. #### Specify positional parameters, but don't use them -When writing at the command line, positional parameters are a blessing, but they can be confusing for future readers. You should always expose your parameters positionally when it makes sense, but you should rarely share a script that pases parameters positionally. +When writing at the command line, positional parameters are a blessing, but they can be confusing for future readers. You should always expose your parameters positionally when it makes sense, but you should rarely share a script that passes parameters positionally. #### Specify short aliases, but don't use them Again, for the sake of typing, it's particularly useful if you can provide two-letter aliases for each of your parameters such that every parameter has a two-letter or less name which is unique. @@ -155,29 +150,26 @@ ValueFromPipelineByPropertyName = $true, HelpText = 'The name of the file to rea [String]$File ``` - #### Use PsCmdlet.ThrowTerminatingError rather than throw #### Use PsCmdlet.WriteError rather than Write-Error Discuss: These need example output to explain why they're better Discuss: a common template (from the Microsoft team) for throwing errors ``` -# Utility to throw an errorrecord -function ThrowError -{ - param - ( - [parameter(Mandatory = $true)] +# Utility to throw an error record +function ThrowError { + param( + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCmdlet] $CallerPSCmdlet, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ExceptionName, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ExceptionMessage, @@ -185,42 +177,42 @@ function ThrowError [System.Object] $ExceptionObject, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ErrorId, - [parameter(Mandatory = $true)] + [Parameter(Mandatory = $true)] [ValidateNotNull()] [System.Management.Automation.ErrorCategory] $ErrorCategory ) - $exception = New-Object $ExceptionName $ExceptionMessage; + $exception = New-Object -TypeName $ExceptionName -ArgumentList $ExceptionMessage $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject $CallerPSCmdlet.ThrowTerminatingError($errorRecord) } ``` - #### Use SupportsShouldProcess when appropriate -Discuss: when is this critical (-whatif) and optional (-confirm_ -Discuss: when should you call PSCmdlet.ShouldProcess vs PSCmdlet.ShouldContinue (-Force) +Discuss: when is this critical (-WhatIf) and optional (-Confirm) +Discuss: when should you call $PSCmdlet.ShouldProcess vs $PSCmdlet.ShouldContinue (-Force) ``` [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "Medium")] param([Switch]$Force) -$RejectAll = $false; -$ConfirmAll = $false; +$RejectAll = $false +$ConfirmAll = $false -foreach($file in ls) { +foreach ($file in ls) { - if($PSCmdlet.ShouldProcess( "Removed the file '$($file.Name)'", + if($PSCmdlet.ShouldProcess("Removed the file '$($file.Name)'", "Remove the file '$($file.Name)'?", - "Removing Files" )) { + "Removing Files") + ) { - if($Force -Or $PSCmdlet.ShouldContinue("Are you REALLY sure you want to remove '$($file.Name)'?", "Removing '$($file.Name)'", [ref]$ConfirmAll, [ref]$RejectAll)) { + if ($Force -Or $PSCmdlet.ShouldContinue("Are you REALLY sure you want to remove '$($file.Name)'?", "Removing '$($file.Name)'", [ref]$ConfirmAll, [ref]$RejectAll)) { "Removing $File" @@ -242,7 +234,6 @@ The problems with this have gone away with autoloading, and this is the only way #### Persisting Configuration My choice: Configuration module. Otherwise, use clixml (or XAML) to persist to AppData (TEST: you shouldn't store configuration in your module folder, as it may not survive upgrades (in PowerShell 3 & 4 there was no side-by-side loading)) - #### Provide aliases in your modules You should feel free to create and use aliases within your modules. In some cases, you can even improve readability by using an alias without the verb, or shortening command names. @@ -250,28 +241,23 @@ For exported aliases, follow the guidance of Microsoft ("ip" for import, "s" for Use `New-Alias ... -ErrorAction SilentlyContinue` to avoid overwriting existing aliases. - - - ### GOTCHAS -#### Beware string concatenation with + -You should always wrap this with parentheses, because otherwise it can break (e.g. when passing a string as a parameter. +#### Beware of string concatenation with + +You should always wrap this with parentheses, because otherwise it can break (e.g., when passing a string as a parameter). -#### Beware -match and -like +#### Beware of -match and -like They quietly cast objects to strings (or arrays of strings) -#### Beware -contains and -in -They work on ARRAYS not strings - +#### Beware of -contains and -in +They work on ARRAYS, not strings ### Use Language Features -When writing scripts (as opposed to at the command line), you should almost always choose language features over cmdlets. This includes using if instead of where-object, foreach instead of ForEach-Object, etc. - -The language features are always faster, and almost always more readable. Of course, there are always exceptions, and one exception to this rule is when using foreach will force you to collect a lot of items into an array instead of iterating them as they stream through a pipleine. +When writing scripts (as opposed to at the command line), you should almost always choose language features over cmdlets. This includes using `if` instead of `Where-Object`, `foreach` instead of `ForEach-Object`, etc. +The language features are always faster, and almost always more readable. Of course, there are always exceptions, and one exception to this rule is when using `foreach` will force you to collect a lot of items into an array instead of iterating them as they stream through a pipeline. -### You should understand the .Net underpinnings +### You should understand the .NET underpinnings #### AVOID appending to string in a loop ##### INSTEAD assign output from the loop using $OFS @@ -283,19 +269,17 @@ The language features are always faster, and almost always more readable. Of cou * Joining an array of strings is fast * Easier to read and understand +### Strongly typed parameters +Although PowerShell is a dynamic language, we can specify types, and in parameters, it's particularly useful because it hints to users what they can pass to your command. -### Strongly type parameters -Although PowerShell is a dynamic language, we have can specify types, and in Parameters, it's particularly useful because it hints to users what they can pass to your command. - -Strong types on parameters is also crucial because it's a user-input point. Strong types can help you avoid script injection and various other problems with user inputs, and will allow failures to happen as early as possible (even before your command is called). +Strong types on parameters is also crucial because it's a user-input point. Strong types can help you avoid script injection and various other problems with user inputs, and will allow failures to happen as early as possible (even before your command is called). Additionally, avoid using `[string]` with ParameterSets because anything can be cast to it, so PowerShell can't distinguish one parameter set from the other. -When passing on parameters to another command, you should be _at least_ as strongly typed as the other command, to avoid casting exceptions within your script. +When passing on parameters to another command, they should be _at least_ as strongly typed as the other command, to avoid casting exceptions within your script. One notable exception is when you could accept more than one type. In PowerShell you can specify parameter set overloads, but you can't change the type of a parameter. - ### Don't reinvent the wheel -### Let's talk about Logging -### Let's talk about code signing \ No newline at end of file +### Let's talk about logging +### Let's talk about code signing From 3a00c9018364a9c8502ec94c26b1a7c424516710 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Sun, 16 Apr 2023 12:47:29 +0200 Subject: [PATCH 102/113] Update Writing-Parameter-Blocks.md - Change "what it's expected or allowed values are" to "what its expected or allowed values are" as clearly "it's" cannot mean 'it is' or 'it has' in this context and is thus incorrect. - Change 'You should support --whatif' to 'You should support -WhatIf' as that's the correct casing. Also, the double dash has been changed to a single dash because this is PowerShell, not sh, bash, zsh or something like that. - Remove the unnecessary semicolon at the end of the line in the example code. - Change some small typos and remove more than one blank line in several places. P.S. Consider using single quotes for strings with constant values instead of double quotes! Using PSScriptAnalyzer will catch these! By the way, consider recommending using PSScriptAnalyzer, because using that will generally improve one's code significantly! --- Best-Practices/Writing-Parameter-Blocks.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Best-Practices/Writing-Parameter-Blocks.md b/Best-Practices/Writing-Parameter-Blocks.md index 63f3133..4718f6f 100644 --- a/Best-Practices/Writing-Parameter-Blocks.md +++ b/Best-Practices/Writing-Parameter-Blocks.md @@ -36,7 +36,7 @@ function Test-Help { ``` ### Always Document Every Parameter -You should always provide at least a brief explanation of each parameter, what it's expected or allowed values are, etc. +You should always provide at least a brief explanation of each parameter, what its expected or allowed values are, etc. The best place for this is a simple comment directly above the parameter (inside the param block) so you don't forget to update it if you remove, rename, or otherwise change the parameter, but you can also place them in the comment help block by using `.PARAMETER ParameterName` and writing the help on the next line. @@ -50,9 +50,9 @@ There are a few specific advanced cases where you might want to write an old-fas If you have more than one ParameterSetName on your parameters, you should specify one of them as the `DefaultParameterSetName` in the CmdletBinding. -## You should support --whatif +## You should support -WhatIf -If you write a command that changes state, you should probably add `SupportsShouldProcess` to your CmdletBinding. This allows users to specify `-WhatIf` and `-Confirm` when calling your command, so you'll need to actually support those by using `$PSCmdlet.ShouldProcess(...)` or `$PSCmdlet.ShouldContinue(...)` or by passing the preference variable on to other commands you're calling (e.g. `-Whatif:$WhatIfPreference`). +If you write a command that changes state, you should probably add `SupportsShouldProcess` to your CmdletBinding. This allows users to specify `-WhatIf` and `-Confirm` when calling your command, so you'll need to actually support those by using `$PSCmdlet.ShouldProcess(...)` or `$PSCmdlet.ShouldContinue(...)` or by passing the preference variable on to other commands you're calling (e.g., `-Whatif:$WhatIfPreference`). Here's an example of what that might look like: @@ -63,13 +63,13 @@ Here's an example of what that might look like: param([switch]$Force) # You need to pre-define these (because they're passed by [ref]) -$RejectAll = $false; -$ConfirmAll = $false; +$RejectAll = $false +$ConfirmAll = $false # Note: please don't actually do this with services, restarting them in non-dependency order would be a nightmare... foreach ($service in Get-Service | Where Status -eq "Running") { # This will normally automatically be TRUE. It will only query if the user: - # 1. Has their $ConfirmPreference (default High) set LOWER or equal to the ConfirmImpact in the cmdlet binding (default Medium) + # 1. Has their $ConfirmPreference (default High) set LOWER than or equal to the ConfirmImpact in the cmdlet binding (default Medium) # 2. Passes -Confirm, which sets the $ConfirmPreference in the function's scope to Low if ($PSCmdlet.ShouldProcess( "Restarted the service '$($service.Name)'", "Restart the '$($service.DisplayName)' service ($($service.Name))?", @@ -93,13 +93,11 @@ foreach ($service in Get-Service | Where Status -eq "Running") { Although PowerShell is a dynamic language, we can specify types, and in parameters, it's particularly useful. - First, because it hints to users what sort of values they can pass to your command. Is it numeric? Text? An object? Second, because using types on parameters helps validate the input, which is crucial because parameters are where you get your user input. Strong types can help you avoid code injection and other problems with user inputs, and will allow failures to happen as early as possible (even before your command is called). -Additionally, when passing on parameters to another command, you should be _at least_ as strongly typed as the other command, to avoid casting exceptions within your script. - +Additionally, when passing on parameters to another command, they should be _at least_ as strongly typed as the other command, to avoid casting exceptions within your script. ### Be careful with `[string]` or `[object]` (and `[PSObject]`) @@ -122,9 +120,8 @@ Parameters of type `[switch]` support passing as switches (without a value), and - Switch parameters should be treated as boolean values in your scripts. Corrolary: you should not write logic that depends on whether or not the user explicitly passed a value to a switch -- do not attempt to treat a switch as having three states! - When you need to pass the value of a switch on to another command, you can either splat it, or specify it using the colon syntax for parameters, as in `-TheirSwitch:$MySwitch` - ## Be generous with accept ValueFromPipelineByPropertyName For the most flexibility, whenever it's practical, you should write your commands to accept their parameters from the pipeline _by property name_. To enhance your ability to match objects, you can add aliases for the parameter name using the `[Alias()]` attribute. -Don't forget that values set from the pipeline are only available in the `process` block. \ No newline at end of file +Don't forget that values set from the pipeline are only available in the `process` block. From 7abe5975b360b4bbf3b104ffa19afe4205769525 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Tue, 25 Apr 2023 08:41:22 +0200 Subject: [PATCH 103/113] Change small typo Changed 'to suggests' to 'to suggest'. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4751222..ef4612c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ ## Contributing -Are you interested in helping create the style guide, or do you just want to change a specific rule? You may use the issues system or Github pull requests freely to suggests corrections and simple changes to rules. However, if you want to add a completely new rule, or totally change a rule, please open an issue and let us discuss it! +Are you interested in helping create the style guide, or do you just want to change a specific rule? You may use the issues system or Github pull requests freely to suggest corrections and simple changes to rules. However, if you want to add a completely new rule, or totally change a rule, please open an issue and let us discuss it! ### Tone From ce29b66de8d0e0c6a751bb206e769205c3773c54 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Tue, 25 Apr 2023 08:47:35 +0200 Subject: [PATCH 104/113] Rename Language-Interop-and-.Net.md to Language-Interop-and-.NET.md Because '.Net' should be written as '.NET', I've already made a pull request (which has been merged since then) to change that in the text. In order to keep it consistent, now also changing it in the name of the document itself. --- ...{Language-Interop-and-.Net.md => Language-Interop-and-.NET.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Best-Practices/{Language-Interop-and-.Net.md => Language-Interop-and-.NET.md} (100%) diff --git a/Best-Practices/Language-Interop-and-.Net.md b/Best-Practices/Language-Interop-and-.NET.md similarity index 100% rename from Best-Practices/Language-Interop-and-.Net.md rename to Best-Practices/Language-Interop-and-.NET.md From 3d98c433df2f5b32c939adff1c833db377e8a7fe Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Tue, 25 Apr 2023 08:53:00 +0200 Subject: [PATCH 105/113] Change link to renamed Markdown file As I've renamed the Markdown file 'Best-Practices/Language-Interop-and-.Net.md' to 'Best-Practices/Language-Interop-and-.NET.md' because the correct casing is '.NET', also renaming the link to this file. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 56bf9fd..8cdf165 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The guidelines are divided into these sections: * [Error Handling](Best-Practices/Error-Handling.md) * [Performance](Best-Practices/Performance.md) * [Security](Best-Practices/Security.md) - * [Language, Interop and .Net](Best-Practices/Language-Interop-and-.Net.md) + * [Language, Interop and .Net](Best-Practices/Language-Interop-and-.NET.md) * [Metadata, Versioning, and Packaging](Best-Practices/Metadata-Versioning-and-Packaging.md) ### Current State: From 23ba4d86e3021cb6c2d872839af54accc8c6961a Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Tue, 25 Apr 2023 08:55:52 +0200 Subject: [PATCH 106/113] Rename link, but now throughout I've renamed the link to the renamed file 'Best-Practices/Language-Interop-and-.NET.md' already, but forgot to also change the text of the link. This has now been corrected. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8cdf165..370773b 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The guidelines are divided into these sections: * [Error Handling](Best-Practices/Error-Handling.md) * [Performance](Best-Practices/Performance.md) * [Security](Best-Practices/Security.md) - * [Language, Interop and .Net](Best-Practices/Language-Interop-and-.NET.md) + * [Language, Interop and .NET](Best-Practices/Language-Interop-and-.NET.md) * [Metadata, Versioning, and Packaging](Best-Practices/Metadata-Versioning-and-Packaging.md) ### Current State: From 813a5c96a2a2fc62f578bb13a6e651f2170f0589 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Tue, 25 Apr 2023 08:57:48 +0200 Subject: [PATCH 107/113] Add a period to the end of a few sentences --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 370773b..5e7627e 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ The guidelines are divided into these sections: Remember [what we mean by _Best Practices_](#what-are-best-practices) -The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting [https://github.com/PoshCode/PowerShellPracticeAndStyle](https://github.com/PoshCode/PowerShellPracticeAndStyle) +The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting [https://github.com/PoshCode/PowerShellPracticeAndStyle](https://github.com/PoshCode/PowerShellPracticeAndStyle). The *PowerShell Style Guide* in particular is in PREVIEW, and we are still actively working out our disagreements about the rules in the guide through the GitHub issues system. @@ -58,11 +58,11 @@ The *PowerShell Style Guide* in particular is in PREVIEW, and we are still activ Please use the issues system or GitHub pull requests to make corrections, contributions, and other changes to the text - we welcome your contributions! -For more information, see [CONTRIBUTING](CONTRIBUTING.md) +For more information, see [CONTRIBUTING](CONTRIBUTING.md). #### Credits -_The Community Book of PowerShell Practices_ was originally compiled and edited by Don Jones and Matt Penny with input from the Windows PowerShell community on PowerShell.org +_The Community Book of PowerShell Practices_ was originally compiled and edited by Don Jones and Matt Penny with input from the Windows PowerShell community on PowerShell.org. Portions copyright (c) Don Jones, Matt Penny, 2014-2015 From f11cb1c96dc717737cf3d4a58540ae00304de452 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Tue, 25 Apr 2023 09:00:12 +0200 Subject: [PATCH 108/113] Add a missing period at the end of a sentence --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e7627e..e9019bb 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ The guidelines are divided into these sections: ### Current State: -Remember [what we mean by _Best Practices_](#what-are-best-practices) +Remember [what we mean by _Best Practices_](#what-are-best-practices). The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting [https://github.com/PoshCode/PowerShellPracticeAndStyle](https://github.com/PoshCode/PowerShellPracticeAndStyle). From 8307c07c0add7483a6b88a16eaf2cb7b28f91cef Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Tue, 25 Apr 2023 09:05:36 +0200 Subject: [PATCH 109/113] Change link to renamed Markdown file As I've renamed the Markdown file 'Best-Practices/Language-Interop-and-.Net.md' to 'Best-Practices/Language-Interop-and-.NET.md' because the correct casing is '.NET', also renaming the link to this file. --- TableOfContents.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TableOfContents.md b/TableOfContents.md index 2798faa..2d89708 100644 --- a/TableOfContents.md +++ b/TableOfContents.md @@ -23,5 +23,5 @@ PowerShell Practice and Style Guide * [Error Handling](Best-Practices/Error-Handling.md) * [Performance](Best-Practices/Performance.md) * [Security](Best-Practices/Security.md) -* [Language, Interop and .Net](Best-Practices/Language-Interop-and-.Net.md) +* [Language, Interop and .NET](Best-Practices/Language-Interop-and-.NET.md) * [Metadata, Versioning, and Packaging](Best-Practices/Metadata-Versioning-and-Packaging.md) From 4aa8272a35e860a5adee1aed94baee1b29cd71f1 Mon Sep 17 00:00:00 2001 From: Richard Vrijhof Date: Mon, 1 May 2023 23:47:17 +0200 Subject: [PATCH 110/113] Add period at the end of a few sentences --- LICENSE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 6c3ab47..5b474fd 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -13,7 +13,7 @@ The authors encourage you to redistribute this content as widely as possible, bu #### Credits -_The Community Book of PowerShell Practices_ was originally compiled and edited by Don Jones and Matt Penny with input from the Windows PowerShell community on PowerShell.org +_The Community Book of PowerShell Practices_ was originally compiled and edited by Don Jones and Matt Penny with input from the Windows PowerShell community on PowerShell.org. Portions copyright (c) Don Jones, Matt Penny, 2014-2015 @@ -27,7 +27,7 @@ Portions copyright (c) Joel Bennett, 2015 #### NOTE -The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting [https://github.com/PoshCode/PowerShellPracticeAndStyle](https://github.com/PoshCode/PowerShellPracticeAndStyle) +The *PowerShell Best Practices* are always evolving, and continue to be edited and updated as the language and tools (and our community understanding of them) evolve. We encourage you to check back for new editions at least twice a year, by visiting [https://github.com/PoshCode/PowerShellPracticeAndStyle](https://github.com/PoshCode/PowerShellPracticeAndStyle). The *PowerShell Style Guide* in particular is in PREVIEW, and we are still actively working out our disagreements about the rules in the guide through the GitHub issues system. Please don't be surprised if over then next few weeks we change rules to contradict what they say at this current moment. From d089a05dc911254c3e35e251f4146f90e8f9c928 Mon Sep 17 00:00:00 2001 From: Spiral Chaotic <25832466+Nevember@users.noreply.github.com> Date: Sun, 7 May 2023 23:37:30 -0400 Subject: [PATCH 111/113] Update Introduction.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Line 24: A) The change of case from .Net to .NET in #163 broke the link on this line. B) Added an Oxford comma after "Interop" to match the format of line 25. (An alternative fix, of course, would be to leave this as-is and remove the Oxford comma on line 25; it all depends on your stance regarding this hotly-debated punctuation mark. ☺ ) --- Best-Practices/Introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Best-Practices/Introduction.md b/Best-Practices/Introduction.md index bb5720d..1955546 100644 --- a/Best-Practices/Introduction.md +++ b/Best-Practices/Introduction.md @@ -21,5 +21,5 @@ One final note about these Best Practices: the perspective of these guidelines h - [Error Handling](Error-Handling.md) - [Performance](Performance.md) - [Security](Security.md) -- [Language, Interop and .Net](Language-Interop-and-.Net.md) +- [Language, Interop, and .NET](Language-Interop-and-.NET.md) - [Metadata, Versioning, and Packaging](Metadata-Versioning-and-Packaging.md) From e5f3e7057591117a65af51a980e7e2107d56f687 Mon Sep 17 00:00:00 2001 From: Spiral Chaotic <25832466+Nevember@users.noreply.github.com> Date: Mon, 8 May 2023 11:54:23 -0400 Subject: [PATCH 112/113] Update Introduction.md (again) Line 5: updated TechNet link and text, pointing now to Microsoft Learn Code Samples. Line 11: capitalized "Style Guide" and linked to its introduction page. --- Best-Practices/Introduction.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Best-Practices/Introduction.md b/Best-Practices/Introduction.md index 1955546..ca8cad6 100644 --- a/Best-Practices/Introduction.md +++ b/Best-Practices/Introduction.md @@ -2,13 +2,13 @@ ## Foreword -If you scan through code projects on [PoshCode](https://github.com/PoshCode) or the [Technet ScriptCenter Gallery](http://gallery.technet.microsoft.com/scriptcenter), it will be immediately apparent that people in the PowerShell community have vastly different ideas about what's "right and wrong" in the world of PowerShell scripting. +If you scan through code projects on [PoshCode](https://github.com/PoshCode) or the [Microsoft Learn Code Samples Gallery](https://learn.microsoft.com/en-us/samples/browse/?languages=powershell), it will be immediately apparent that people in the PowerShell community have vastly different ideas about what's "right and wrong" in the world of PowerShell scripting. Over the years several attempts have been made to arrive at a consensus, most notably the "Great Debate" series of blog posts on [PowerShell.org](https://powershell.org/?s=great+debate) following the 2013 Scripting Games, which outlined some of the more controversial issues and asked for community discussions. This project has been created, in part, as a public place to continue those debates as well as to document the consensus of the community when we _do_ arrive at a consensus. -Remember that best practices are not hard-and-fast rules. We don't even consider them as solid as the style guide rules. They are the things you should _usually_ do as a starting point, and should deviate from when it's appropriate. +Remember that best practices are not hard-and-fast rules. We don't even consider them as solid as the [Style Guide](../Style-Guide/Introduction.md) rules. They are the things you should _usually_ do as a starting point, and should deviate from when it's appropriate. One final note about these Best Practices: the perspective of these guidelines has so-far been strongly influenced by system administrator practitioners, less-so by language geeks and developers. We're trying to balance that and provide perspective, but when it comes to best practices, we definitely allow the experience of administrators to drive what the PowerShell community considers best and worst practices -- so if you are working from a different perspective, you'll have to take all of this with a grain of salt. From c26201fe7197c8196b2828c82c749ca865264e39 Mon Sep 17 00:00:00 2001 From: Spiral Chaotic <25832466+Nevember@users.noreply.github.com> Date: Mon, 8 May 2023 12:03:49 -0400 Subject: [PATCH 113/113] Update CONTRIBUTING.md Line 36: Echoing changes made to `/Best-Practices/Introduction.md`, I capitalized .NET in the link to `Language-Interop-and-.NET.md`, reflecting the recent change to that document's name, and I added a comma after the word "Interop," akin to the list of items on line 37. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ef4612c..084a63b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,7 @@ To repeat from the ReadMe, the guidelines are divided into these sections: * [Error Handling](Best-Practices/Error-Handling.md) * [Performance](Best-Practices/Performance.md) * [Security](Best-Practices/Security.md) - * [Language, Interop and .Net](Best-Practices/Language-Interop-and-.Net.md) + * [Language, Interop, and .NET](Best-Practices/Language-Interop-and-.NET.md) * [Metadata, Versioning, and Packaging](Best-Practices/Metadata-Versioning-and-Packaging.md) Markdown documents on GitHub support linking within a document, but only to headers, so when editing, in addition to keeping practices and guidelines in the documents where they make sense, please use headlines for each guideline, and lower level headlines for rationale, examples, counter examples, and exceptions.