diff --git a/reference/5.1/Microsoft.PowerShell.Core/About/about_Switch.md b/reference/5.1/Microsoft.PowerShell.Core/About/about_Switch.md index 38d8e8e48832..e75344bffb0f 100644 --- a/reference/5.1/Microsoft.PowerShell.Core/About/about_Switch.md +++ b/reference/5.1/Microsoft.PowerShell.Core/About/about_Switch.md @@ -1,7 +1,7 @@ --- description: Explains how to use a switch to handle multiple `if` statements. Locale: en-US -ms.date: 05/15/2025 +ms.date: 05/19/2025 online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_switch?view=powershell-5.1&WT.mc_id=ps-gethelp schema: 2.0.0 title: about_Switch @@ -20,11 +20,11 @@ variables and the properties of objects. To check multiple conditions, you can use a `switch` statement. The `switch` statement is similar to a series of `if` statements, but it's simpler. The -`switch` statement lists each condition and an optional action. If a condition -matches, the action is performed. +`switch` statement lists each condition and the corresponding action. If a +condition matches, the action is performed. -The `switch` statement can use the `$_` and `$switch` automatic variables. For -more information, see [about_Automatic_Variables][03]. +> [!IMPORTANT] +> The `switch` statement converts all values to strings before comparison. ## Syntax @@ -37,33 +37,33 @@ switch () { } ``` -This is similar to the following `if` statements: +The syntax of a `switch` statement is similar to the following `if` statements: ```Syntax -if ( -eq ()) {} -if ( -eq ()) {} +if ("$()") -eq ("$()") {} +if ("$()") -eq ("$()") {} ``` -The `` is single expression that's evaluated in expression -mode to return a value. - -The `` is an expression whose value is compared to the -input value. Expressions include literal values (strings or numbers), -variables, and scriptblocks that return a boolean value. For an example, see +Expressions include literal values (strings or numbers), variables, and +scriptblocks that return a boolean value. The `switch` statement converts all +values to strings before comparison. For an example, see [Impact of string conversion][02] later in this article. -It's important to understand that the `` value is on the -left-hand side of the comparison expression. That means the result of the -`` is on the right-hand side, which can be converted to the -type of the left-hand side value for comparison. For more information, see -[about_Comparison_Operators][05] +The `` is evaluated in expression mode. If the expression +returns more than one value, such as an array or other enumerable type, the +`switch` statement evaluates each enumerated value separately. + +The `` is an expression must resolve to a single value. +That value is compared to the input value. The value `default` is reserved for the action used when there are no other matches. -The `$_` automatic variable contains the value of the expression passed to the -`switch` statement and is available for evaluation and use within the scope of -the `` statements. +The `switch` statement can use the `$_` and `$switch` automatic variables. The +automatic variable contains the value of the expression passed to the `switch` +statement and is available for evaluation and use within the scope of the +`` statements. For more information, see +[about_Automatic_Variables][03]. The complete `switch` statement syntax is as follows: @@ -109,7 +109,8 @@ conditions. It's equivalent to an `else` clause in an `if` statement. Only one file is read a line at a time and evaluated by the `switch` statement. By default, the comparison is case-insensitive. The **File** parameter only supports one file. If multiple **File** parameters are included, only the - last one is used. For more information see [File parameter examples][01]. + last one is used. For more information see the + [**File** parameter examples][01]. - **Regex** - Performs regular expression matching of the value to the condition. If the match clause isn't a string, this parameter is ignored. The comparison is case-insensitive. The `$Matches` automatic variable is @@ -121,7 +122,11 @@ conditions. It's equivalent to an `else` clause in an `if` statement. Only one > ignored. Multiple instances of parameters are also permitted. However, only > the last parameter listed is used. -## Simple match examples +## Examples + +The following examples demonstrate the use of the `switch` statement. + +### Simple match examples In the following example, the `switch` statement compares the test value `3` to each of the conditions. When the test value matches the condition, the action @@ -159,69 +164,44 @@ It's three. Three again. ``` -To direct the `switch` to stop comparing after a match, use the `break` -statement. The `break` statement terminates the `switch` statement. - -```powershell -switch (3) { - 1 { "It's one." } - 2 { "It's two." } - 3 { "It's three."; break } - 4 { "It's four." } - 3 { "Three again." } -} -``` - -```Output -It's three. -``` +### Use `break` and `continue` to control the flow -If the test value is a collection, such as an array, each item in the -collection is evaluated in the order in which it appears. The following -examples evaluates 4 and then 2. +If the value matches multiple conditions, the action for each condition is +executed. To change this behavior, use the `break` or `continue` keywords. -```powershell -switch (4, 2) { - 1 { "It's one." } - 2 { "It's two." } - 3 { "It's three." } - 4 { "It's four." } - 3 { "Three again." } -} -``` +The `break` keyword stops processing and exits the `switch` statement. -```Output -It's four. -It's two. -``` +The `continue` keyword stops processing the current value, but continues +processing any subsequent values. -Any `break` statements apply to the collection, not to each value, as shown -in the following example. The `switch` statement is terminated by the `break` -statement in the condition of value 4. +The following example processes an array of numbers and displays if they're +odd or even. Negative numbers are skipped with the `continue` keyword. If a +non-number is encountered, execution is terminated with the `break` keyword. ```powershell -switch (4, 2) { - 1 { "It's one."; break } - 2 { "It's two." ; break } - 3 { "It's three." ; break } - 4 { "It's four." ; break } - 3 { "Three again." } +switch (1,4,-1,3,"Hello",2,1) { + {$_ -lt 0} { continue } + {$_ -isnot [int32]} { break } + {$_ % 2} { "$_ is Odd" } + {-not ($_ % 2)} { "$_ is Even" } } ``` ```Output -It's four. +1 is Odd +4 is Even +3 is Odd ``` -## Impact of string conversion +### Impact of string conversion -Any unquoted value that's not recognized as a number is treated as a string. To -avoid unintended string conversion, use script blocks to evaluate the switch -value. +All values, both input and the comparison value are converted to strings for +comparison. To avoid unintended string conversion, use script blocks to +evaluate the switch value. ```powershell switch ( ([datetime]'1 Jan 1970').DayOfWeek ) { - 4 { 'The value matches a Thursday.' } + 4 { 'The integer value matches a Thursday.' } "4" { 'The numeric string matches a Thursday.' } "Thursday" { 'The string value matches a Thursday.' } { 4 -eq $_ } { 'The expression matches a Thursday.' } @@ -242,7 +222,7 @@ statement. ```powershell if (4 -eq ([datetime]'1 Jan 1970').DayOfWeek) { - 'The value matches a Thursday.' + 'The integer value matches a Thursday.' } ``` @@ -250,40 +230,26 @@ if (4 -eq ([datetime]'1 Jan 1970').DayOfWeek) { The value matches a Thursday. ``` -## More complex match examples - -In this example, the `switch` statement is testing for the type of the value in -the hashtable. You must use and expression that returns a boolean value to -select the scriptblock to execute. +In this example, a hashtable is passed to the `switch` statement. The `switch` +converts the hashtable to a string. ```powershell -$var = @{A = 10; B = 'abc'} - -foreach ($key in $var.Keys) { - switch ($var[$key].GetType()) { - { $_ -eq [int32] } { "$key + 10 = $($var[$key] + 10)" } - { $_ -eq [string] } { "$key = $($var[$key])" } - } +$test = @{ + Test = 'test' + Test2 = 'test2' } + +$test.ToString() ``` ```Output -A + 10 = 20 -B = abc +System.Collections.Hashtable ``` -In this example, an object that's not a string or numerical data is passed to -the `switch`. The `switch` performs a string coercion on the object and -evaluates the outcome. +Notice that the string representation of the hashtable is not the same as the +value of the **Test** key. ```powershell -$test = @{ - Test = 'test' - Test2 = 'test2' -} - -$test.ToString() - switch -Exact ($test) { 'System.Collections.Hashtable' { 'Hashtable string coercion' } 'test' { 'Hashtable value' } @@ -291,10 +257,34 @@ switch -Exact ($test) { ``` ```Output -System.Collections.Hashtable Hashtable string coercion ``` +### Use `switch` to test the values in a hashtable + +In this example, the `switch` statement is testing for the type of the value in +the hashtable. We must enumerate the items in the hashtable before we can test +the values. To avoid the complications of string conversion, use a script block +that returns a boolean value to select the action scriptblock to execute. + +```powershell +$var = @{A = 10; B = 'abc'} + +foreach ($key in $var.Keys) { + switch ($var[$key].GetType()) { + { $_ -eq [int32] } { "$key + 10 = $($var[$key] + 10)" } + { $_ -eq [string] } { "$key = $($var[$key])" } + } +} +``` + +```Output +A + 10 = 20 +B = abc +``` + +### Use wildcards with `switch` + In this example, there is no matching case so there is no output. ```powershell @@ -342,6 +332,8 @@ switch -Wildcard ("fourteen") { That's too many. ``` +### Use regular expressions with `switch` + The following example uses the `-Regex` parameter. ```powershell @@ -400,34 +392,7 @@ switch ((Get-Date 1-Jan-2022), (Get-Date 25-Dec-2021)) { } ``` -If the value matches multiple conditions, the action for each condition is -executed. To change this behavior, use the `break` or `continue` keywords. - -The `break` keyword stops processing and exits the `switch` statement. - -The `continue` keyword stops processing the current value, but continues -processing any subsequent values. - -The following example processes an array of numbers and displays if they're -odd or even. Negative numbers are skipped with the `continue` keyword. If a -non-number is encountered, execution is terminated with the `break` keyword. - -```powershell -switch (1,4,-1,3,"Hello",2,1) { - {$_ -lt 0} { continue } - {$_ -isnot [int32]} { break } - {$_ % 2} { "$_ is Odd" } - {-not ($_ % 2)} { "$_ is Even" } -} -``` - -```Output -1 is Odd -4 is Even -3 is Odd -``` - -## File parameter examples +### Read the content of a file with `switch` Using the `switch` statement with the **File** parameter is an efficient way to process large files line by line. PowerShell streams the lines of the file to @@ -450,10 +415,10 @@ switch -Regex -File .\README.md { } ``` -The `` argument is interpreted as a wildcard expression, but it must -match only one file. The following example is the same as the previous one -except it uses a wildcard in the `` argument. This example works -because the wildcard pattern matches only one file. +The `` argument accepts wildcard expressions, but it must match only +one file. The following example is the same as the previous one except it uses +a wildcard in the `` argument. This example works because the +wildcard pattern matches only one file. ```powershell switch -Regex -File .\README.* { @@ -478,16 +443,15 @@ switch -File $fileEscaped { foo { 'Foo' } } ## See also - [about_Break][04] -- [about_Continue][06] -- [about_If][07] -- [about_Script_Blocks][08] +- [about_Continue][05] +- [about_If][06] +- [about_Script_Blocks][07] - -[01]: #file-parameter-examples + +[01]: #read-the-content-of-a-file-with-switch [02]: #impact-of-string-conversion [03]: about_Automatic_Variables.md [04]: about_break.md -[05]: about_Comparison_Operators.md -[06]: about_Continue.md -[07]: about_If.md -[08]: about_Script_Blocks.md +[05]: about_Continue.md +[06]: about_If.md +[07]: about_Script_Blocks.md diff --git a/reference/7.4/Microsoft.PowerShell.Core/About/about_Switch.md b/reference/7.4/Microsoft.PowerShell.Core/About/about_Switch.md index c96547076582..9d57505d23e2 100644 --- a/reference/7.4/Microsoft.PowerShell.Core/About/about_Switch.md +++ b/reference/7.4/Microsoft.PowerShell.Core/About/about_Switch.md @@ -1,7 +1,7 @@ --- description: Explains how to use a switch to handle multiple `if` statements. Locale: en-US -ms.date: 05/15/2025 +ms.date: 05/19/2025 online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_switch?view=powershell-7.4&WT.mc_id=ps-gethelp schema: 2.0.0 title: about_Switch @@ -20,11 +20,11 @@ variables and the properties of objects. To check multiple conditions, you can use a `switch` statement. The `switch` statement is similar to a series of `if` statements, but it's simpler. The -`switch` statement lists each condition and an optional action. If a condition -matches, the action is performed. +`switch` statement lists each condition and the corresponding action. If a +condition matches, the action is performed. -The `switch` statement can use the `$_` and `$switch` automatic variables. For -more information, see [about_Automatic_Variables][03]. +> [!IMPORTANT] +> The `switch` statement converts all values to strings before comparison. ## Syntax @@ -37,33 +37,33 @@ switch () { } ``` -This is similar to the following `if` statements: +The syntax of a `switch` statement is similar to the following `if` statements: ```Syntax -if ( -eq ()) {} -if ( -eq ()) {} +if ("$()") -eq ("$()") {} +if ("$()") -eq ("$()") {} ``` -The `` is single expression that's evaluated in expression -mode to return a value. - -The `` is an expression whose value is compared to the -input value. Expressions include literal values (strings or numbers), -variables, and scriptblocks that return a boolean value. For an example, see +Expressions include literal values (strings or numbers), variables, and +scriptblocks that return a boolean value. The `switch` statement converts all +values to strings before comparison. For an example, see [Impact of string conversion][02] later in this article. -It's important to understand that the `` value is on the -left-hand side of the comparison expression. That means the result of the -`` is on the right-hand side, which can be converted to the -type of the left-hand side value for comparison. For more information, see -[about_Comparison_Operators][05] +The `` is evaluated in expression mode. If the expression +returns more than one value, such as an array or other enumerable type, the +`switch` statement evaluates each enumerated value separately. + +The `` is an expression must resolve to a single value. +That value is compared to the input value. The value `default` is reserved for the action used when there are no other matches. -The `$_` automatic variable contains the value of the expression passed to the -`switch` statement and is available for evaluation and use within the scope of -the `` statements. +The `switch` statement can use the `$_` and `$switch` automatic variables. The +automatic variable contains the value of the expression passed to the `switch` +statement and is available for evaluation and use within the scope of the +`` statements. For more information, see +[about_Automatic_Variables][03]. The complete `switch` statement syntax is as follows: @@ -109,7 +109,8 @@ conditions. It's equivalent to an `else` clause in an `if` statement. Only one file is read a line at a time and evaluated by the `switch` statement. By default, the comparison is case-insensitive. The **File** parameter only supports one file. If multiple **File** parameters are included, only the - last one is used. For more information see [File parameter examples][01]. + last one is used. For more information see the + [**File** parameter examples][01]. - **Regex** - Performs regular expression matching of the value to the condition. If the match clause isn't a string, this parameter is ignored. The comparison is case-insensitive. The `$Matches` automatic variable is @@ -121,7 +122,11 @@ conditions. It's equivalent to an `else` clause in an `if` statement. Only one > ignored. Multiple instances of parameters are also permitted. However, only > the last parameter listed is used. -## Simple match examples +## Examples + +The following examples demonstrate the use of the `switch` statement. + +### Simple match examples In the following example, the `switch` statement compares the test value `3` to each of the conditions. When the test value matches the condition, the action @@ -159,69 +164,44 @@ It's three. Three again. ``` -To direct the `switch` to stop comparing after a match, use the `break` -statement. The `break` statement terminates the `switch` statement. - -```powershell -switch (3) { - 1 { "It's one." } - 2 { "It's two." } - 3 { "It's three."; break } - 4 { "It's four." } - 3 { "Three again." } -} -``` - -```Output -It's three. -``` +### Use `break` and `continue` to control the flow -If the test value is a collection, such as an array, each item in the -collection is evaluated in the order in which it appears. The following -examples evaluates 4 and then 2. +If the value matches multiple conditions, the action for each condition is +executed. To change this behavior, use the `break` or `continue` keywords. -```powershell -switch (4, 2) { - 1 { "It's one." } - 2 { "It's two." } - 3 { "It's three." } - 4 { "It's four." } - 3 { "Three again." } -} -``` +The `break` keyword stops processing and exits the `switch` statement. -```Output -It's four. -It's two. -``` +The `continue` keyword stops processing the current value, but continues +processing any subsequent values. -Any `break` statements apply to the collection, not to each value, as shown -in the following example. The `switch` statement is terminated by the `break` -statement in the condition of value 4. +The following example processes an array of numbers and displays if they're +odd or even. Negative numbers are skipped with the `continue` keyword. If a +non-number is encountered, execution is terminated with the `break` keyword. ```powershell -switch (4, 2) { - 1 { "It's one."; break } - 2 { "It's two." ; break } - 3 { "It's three." ; break } - 4 { "It's four." ; break } - 3 { "Three again." } +switch (1,4,-1,3,"Hello",2,1) { + {$_ -lt 0} { continue } + {$_ -isnot [int32]} { break } + {$_ % 2} { "$_ is Odd" } + {-not ($_ % 2)} { "$_ is Even" } } ``` ```Output -It's four. +1 is Odd +4 is Even +3 is Odd ``` -## Impact of string conversion +### Impact of string conversion -Any unquoted value that's not recognized as a number is treated as a string. To -avoid unintended string conversion, use script blocks to evaluate the switch -value. +All values, both input and the comparison value are converted to strings for +comparison. To avoid unintended string conversion, use script blocks to +evaluate the switch value. ```powershell switch ( ([datetime]'1 Jan 1970').DayOfWeek ) { - 4 { 'The value matches a Thursday.' } + 4 { 'The integer value matches a Thursday.' } "4" { 'The numeric string matches a Thursday.' } "Thursday" { 'The string value matches a Thursday.' } { 4 -eq $_ } { 'The expression matches a Thursday.' } @@ -242,7 +222,7 @@ statement. ```powershell if (4 -eq ([datetime]'1 Jan 1970').DayOfWeek) { - 'The value matches a Thursday.' + 'The integer value matches a Thursday.' } ``` @@ -250,40 +230,26 @@ if (4 -eq ([datetime]'1 Jan 1970').DayOfWeek) { The value matches a Thursday. ``` -## More complex match examples - -In this example, the `switch` statement is testing for the type of the value in -the hashtable. You must use and expression that returns a boolean value to -select the scriptblock to execute. +In this example, a hashtable is passed to the `switch` statement. The `switch` +converts the hashtable to a string. ```powershell -$var = @{A = 10; B = 'abc'} - -foreach ($key in $var.Keys) { - switch ($var[$key].GetType()) { - { $_ -eq [int32] } { "$key + 10 = $($var[$key] + 10)" } - { $_ -eq [string] } { "$key = $($var[$key])" } - } +$test = @{ + Test = 'test' + Test2 = 'test2' } + +$test.ToString() ``` ```Output -A + 10 = 20 -B = abc +System.Collections.Hashtable ``` -In this example, an object that's not a string or numerical data is passed to -the `switch`. The `switch` performs a string coercion on the object and -evaluates the outcome. +Notice that the string representation of the hashtable is not the same as the +value of the **Test** key. ```powershell -$test = @{ - Test = 'test' - Test2 = 'test2' -} - -$test.ToString() - switch -Exact ($test) { 'System.Collections.Hashtable' { 'Hashtable string coercion' } 'test' { 'Hashtable value' } @@ -291,10 +257,34 @@ switch -Exact ($test) { ``` ```Output -System.Collections.Hashtable Hashtable string coercion ``` +### Use `switch` to test the values in a hashtable + +In this example, the `switch` statement is testing for the type of the value in +the hashtable. We must enumerate the items in the hashtable before we can test +the values. To avoid the complications of string conversion, use a script block +that returns a boolean value to select the action scriptblock to execute. + +```powershell +$var = @{A = 10; B = 'abc'} + +foreach ($key in $var.Keys) { + switch ($var[$key].GetType()) { + { $_ -eq [int32] } { "$key + 10 = $($var[$key] + 10)" } + { $_ -eq [string] } { "$key = $($var[$key])" } + } +} +``` + +```Output +A + 10 = 20 +B = abc +``` + +### Use wildcards with `switch` + In this example, there is no matching case so there is no output. ```powershell @@ -342,6 +332,8 @@ switch -Wildcard ("fourteen") { That's too many. ``` +### Use regular expressions with `switch` + The following example uses the `-Regex` parameter. ```powershell @@ -400,34 +392,7 @@ switch ((Get-Date 1-Jan-2022), (Get-Date 25-Dec-2021)) { } ``` -If the value matches multiple conditions, the action for each condition is -executed. To change this behavior, use the `break` or `continue` keywords. - -The `break` keyword stops processing and exits the `switch` statement. - -The `continue` keyword stops processing the current value, but continues -processing any subsequent values. - -The following example processes an array of numbers and displays if they're -odd or even. Negative numbers are skipped with the `continue` keyword. If a -non-number is encountered, execution is terminated with the `break` keyword. - -```powershell -switch (1,4,-1,3,"Hello",2,1) { - {$_ -lt 0} { continue } - {$_ -isnot [int32]} { break } - {$_ % 2} { "$_ is Odd" } - {-not ($_ % 2)} { "$_ is Even" } -} -``` - -```Output -1 is Odd -4 is Even -3 is Odd -``` - -## File parameter examples +### Read the content of a file with `switch` Using the `switch` statement with the **File** parameter is an efficient way to process large files line by line. PowerShell streams the lines of the file to @@ -450,10 +415,10 @@ switch -Regex -File .\README.md { } ``` -The `` argument is interpreted as a wildcard expression, but it must -match only one file. The following example is the same as the previous one -except it uses a wildcard in the `` argument. This example works -because the wildcard pattern matches only one file. +The `` argument accepts wildcard expressions, but it must match only +one file. The following example is the same as the previous one except it uses +a wildcard in the `` argument. This example works because the +wildcard pattern matches only one file. ```powershell switch -Regex -File .\README.* { @@ -478,16 +443,15 @@ switch -File $fileEscaped { foo { 'Foo' } } ## See also - [about_Break][04] -- [about_Continue][06] -- [about_If][07] -- [about_Script_Blocks][08] +- [about_Continue][05] +- [about_If][06] +- [about_Script_Blocks][07] - -[01]: #file-parameter-examples + +[01]: #read-the-content-of-a-file-with-switch [02]: #impact-of-string-conversion [03]: about_Automatic_Variables.md [04]: about_break.md -[05]: about_Comparison_Operators.md -[06]: about_Continue.md -[07]: about_If.md -[08]: about_Script_Blocks.md +[05]: about_Continue.md +[06]: about_If.md +[07]: about_Script_Blocks.md diff --git a/reference/7.5/Microsoft.PowerShell.Core/About/about_Switch.md b/reference/7.5/Microsoft.PowerShell.Core/About/about_Switch.md index be911365e6ad..f38c9ec98cb2 100644 --- a/reference/7.5/Microsoft.PowerShell.Core/About/about_Switch.md +++ b/reference/7.5/Microsoft.PowerShell.Core/About/about_Switch.md @@ -1,7 +1,7 @@ --- description: Explains how to use a switch to handle multiple `if` statements. Locale: en-US -ms.date: 05/15/2025 +ms.date: 05/19/2025 online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_switch?view=powershell-7.5&WT.mc_id=ps-gethelp schema: 2.0.0 title: about_Switch @@ -20,11 +20,11 @@ variables and the properties of objects. To check multiple conditions, you can use a `switch` statement. The `switch` statement is similar to a series of `if` statements, but it's simpler. The -`switch` statement lists each condition and an optional action. If a condition -matches, the action is performed. +`switch` statement lists each condition and the corresponding action. If a +condition matches, the action is performed. -The `switch` statement can use the `$_` and `$switch` automatic variables. For -more information, see [about_Automatic_Variables][03]. +> [!IMPORTANT] +> The `switch` statement converts all values to strings before comparison. ## Syntax @@ -37,33 +37,33 @@ switch () { } ``` -This is similar to the following `if` statements: +The syntax of a `switch` statement is similar to the following `if` statements: ```Syntax -if ( -eq ()) {} -if ( -eq ()) {} +if ("$()") -eq ("$()") {} +if ("$()") -eq ("$()") {} ``` -The `` is single expression that's evaluated in expression -mode to return a value. - -The `` is an expression whose value is compared to the -input value. Expressions include literal values (strings or numbers), -variables, and scriptblocks that return a boolean value. For an example, see +Expressions include literal values (strings or numbers), variables, and +scriptblocks that return a boolean value. The `switch` statement converts all +values to strings before comparison. For an example, see [Impact of string conversion][02] later in this article. -It's important to understand that the `` value is on the -left-hand side of the comparison expression. That means the result of the -`` is on the right-hand side, which can be converted to the -type of the left-hand side value for comparison. For more information, see -[about_Comparison_Operators][05] +The `` is evaluated in expression mode. If the expression +returns more than one value, such as an array or other enumerable type, the +`switch` statement evaluates each enumerated value separately. + +The `` is an expression must resolve to a single value. +That value is compared to the input value. The value `default` is reserved for the action used when there are no other matches. -The `$_` automatic variable contains the value of the expression passed to the -`switch` statement and is available for evaluation and use within the scope of -the `` statements. +The `switch` statement can use the `$_` and `$switch` automatic variables. The +automatic variable contains the value of the expression passed to the `switch` +statement and is available for evaluation and use within the scope of the +`` statements. For more information, see +[about_Automatic_Variables][03]. The complete `switch` statement syntax is as follows: @@ -109,7 +109,8 @@ conditions. It's equivalent to an `else` clause in an `if` statement. Only one file is read a line at a time and evaluated by the `switch` statement. By default, the comparison is case-insensitive. The **File** parameter only supports one file. If multiple **File** parameters are included, only the - last one is used. For more information see [File parameter examples][01]. + last one is used. For more information see the + [**File** parameter examples][01]. - **Regex** - Performs regular expression matching of the value to the condition. If the match clause isn't a string, this parameter is ignored. The comparison is case-insensitive. The `$Matches` automatic variable is @@ -121,7 +122,11 @@ conditions. It's equivalent to an `else` clause in an `if` statement. Only one > ignored. Multiple instances of parameters are also permitted. However, only > the last parameter listed is used. -## Simple match examples +## Examples + +The following examples demonstrate the use of the `switch` statement. + +### Simple match examples In the following example, the `switch` statement compares the test value `3` to each of the conditions. When the test value matches the condition, the action @@ -159,69 +164,44 @@ It's three. Three again. ``` -To direct the `switch` to stop comparing after a match, use the `break` -statement. The `break` statement terminates the `switch` statement. - -```powershell -switch (3) { - 1 { "It's one." } - 2 { "It's two." } - 3 { "It's three."; break } - 4 { "It's four." } - 3 { "Three again." } -} -``` - -```Output -It's three. -``` +### Use `break` and `continue` to control the flow -If the test value is a collection, such as an array, each item in the -collection is evaluated in the order in which it appears. The following -examples evaluates 4 and then 2. +If the value matches multiple conditions, the action for each condition is +executed. To change this behavior, use the `break` or `continue` keywords. -```powershell -switch (4, 2) { - 1 { "It's one." } - 2 { "It's two." } - 3 { "It's three." } - 4 { "It's four." } - 3 { "Three again." } -} -``` +The `break` keyword stops processing and exits the `switch` statement. -```Output -It's four. -It's two. -``` +The `continue` keyword stops processing the current value, but continues +processing any subsequent values. -Any `break` statements apply to the collection, not to each value, as shown -in the following example. The `switch` statement is terminated by the `break` -statement in the condition of value 4. +The following example processes an array of numbers and displays if they're +odd or even. Negative numbers are skipped with the `continue` keyword. If a +non-number is encountered, execution is terminated with the `break` keyword. ```powershell -switch (4, 2) { - 1 { "It's one."; break } - 2 { "It's two." ; break } - 3 { "It's three." ; break } - 4 { "It's four." ; break } - 3 { "Three again." } +switch (1,4,-1,3,"Hello",2,1) { + {$_ -lt 0} { continue } + {$_ -isnot [int32]} { break } + {$_ % 2} { "$_ is Odd" } + {-not ($_ % 2)} { "$_ is Even" } } ``` ```Output -It's four. +1 is Odd +4 is Even +3 is Odd ``` -## Impact of string conversion +### Impact of string conversion -Any unquoted value that's not recognized as a number is treated as a string. To -avoid unintended string conversion, use script blocks to evaluate the switch -value. +All values, both input and the comparison value are converted to strings for +comparison. To avoid unintended string conversion, use script blocks to +evaluate the switch value. ```powershell switch ( ([datetime]'1 Jan 1970').DayOfWeek ) { - 4 { 'The value matches a Thursday.' } + 4 { 'The integer value matches a Thursday.' } "4" { 'The numeric string matches a Thursday.' } "Thursday" { 'The string value matches a Thursday.' } { 4 -eq $_ } { 'The expression matches a Thursday.' } @@ -242,7 +222,7 @@ statement. ```powershell if (4 -eq ([datetime]'1 Jan 1970').DayOfWeek) { - 'The value matches a Thursday.' + 'The integer value matches a Thursday.' } ``` @@ -250,40 +230,26 @@ if (4 -eq ([datetime]'1 Jan 1970').DayOfWeek) { The value matches a Thursday. ``` -## More complex match examples - -In this example, the `switch` statement is testing for the type of the value in -the hashtable. You must use and expression that returns a boolean value to -select the scriptblock to execute. +In this example, a hashtable is passed to the `switch` statement. The `switch` +converts the hashtable to a string. ```powershell -$var = @{A = 10; B = 'abc'} - -foreach ($key in $var.Keys) { - switch ($var[$key].GetType()) { - { $_ -eq [int32] } { "$key + 10 = $($var[$key] + 10)" } - { $_ -eq [string] } { "$key = $($var[$key])" } - } +$test = @{ + Test = 'test' + Test2 = 'test2' } + +$test.ToString() ``` ```Output -A + 10 = 20 -B = abc +System.Collections.Hashtable ``` -In this example, an object that's not a string or numerical data is passed to -the `switch`. The `switch` performs a string coercion on the object and -evaluates the outcome. +Notice that the string representation of the hashtable is not the same as the +value of the **Test** key. ```powershell -$test = @{ - Test = 'test' - Test2 = 'test2' -} - -$test.ToString() - switch -Exact ($test) { 'System.Collections.Hashtable' { 'Hashtable string coercion' } 'test' { 'Hashtable value' } @@ -291,10 +257,34 @@ switch -Exact ($test) { ``` ```Output -System.Collections.Hashtable Hashtable string coercion ``` +### Use `switch` to test the values in a hashtable + +In this example, the `switch` statement is testing for the type of the value in +the hashtable. We must enumerate the items in the hashtable before we can test +the values. To avoid the complications of string conversion, use a script block +that returns a boolean value to select the action scriptblock to execute. + +```powershell +$var = @{A = 10; B = 'abc'} + +foreach ($key in $var.Keys) { + switch ($var[$key].GetType()) { + { $_ -eq [int32] } { "$key + 10 = $($var[$key] + 10)" } + { $_ -eq [string] } { "$key = $($var[$key])" } + } +} +``` + +```Output +A + 10 = 20 +B = abc +``` + +### Use wildcards with `switch` + In this example, there is no matching case so there is no output. ```powershell @@ -342,6 +332,8 @@ switch -Wildcard ("fourteen") { That's too many. ``` +### Use regular expressions with `switch` + The following example uses the `-Regex` parameter. ```powershell @@ -400,34 +392,7 @@ switch ((Get-Date 1-Jan-2022), (Get-Date 25-Dec-2021)) { } ``` -If the value matches multiple conditions, the action for each condition is -executed. To change this behavior, use the `break` or `continue` keywords. - -The `break` keyword stops processing and exits the `switch` statement. - -The `continue` keyword stops processing the current value, but continues -processing any subsequent values. - -The following example processes an array of numbers and displays if they're -odd or even. Negative numbers are skipped with the `continue` keyword. If a -non-number is encountered, execution is terminated with the `break` keyword. - -```powershell -switch (1,4,-1,3,"Hello",2,1) { - {$_ -lt 0} { continue } - {$_ -isnot [int32]} { break } - {$_ % 2} { "$_ is Odd" } - {-not ($_ % 2)} { "$_ is Even" } -} -``` - -```Output -1 is Odd -4 is Even -3 is Odd -``` - -## File parameter examples +### Read the content of a file with `switch` Using the `switch` statement with the **File** parameter is an efficient way to process large files line by line. PowerShell streams the lines of the file to @@ -450,10 +415,10 @@ switch -Regex -File .\README.md { } ``` -The `` argument is interpreted as a wildcard expression, but it must -match only one file. The following example is the same as the previous one -except it uses a wildcard in the `` argument. This example works -because the wildcard pattern matches only one file. +The `` argument accepts wildcard expressions, but it must match only +one file. The following example is the same as the previous one except it uses +a wildcard in the `` argument. This example works because the +wildcard pattern matches only one file. ```powershell switch -Regex -File .\README.* { @@ -478,16 +443,15 @@ switch -File $fileEscaped { foo { 'Foo' } } ## See also - [about_Break][04] -- [about_Continue][06] -- [about_If][07] -- [about_Script_Blocks][08] +- [about_Continue][05] +- [about_If][06] +- [about_Script_Blocks][07] - -[01]: #file-parameter-examples + +[01]: #read-the-content-of-a-file-with-switch [02]: #impact-of-string-conversion [03]: about_Automatic_Variables.md [04]: about_break.md -[05]: about_Comparison_Operators.md -[06]: about_Continue.md -[07]: about_If.md -[08]: about_Script_Blocks.md +[05]: about_Continue.md +[06]: about_If.md +[07]: about_Script_Blocks.md diff --git a/reference/7.6/Microsoft.PowerShell.Core/About/about_Switch.md b/reference/7.6/Microsoft.PowerShell.Core/About/about_Switch.md index 04364df8762b..526fbf960a7f 100644 --- a/reference/7.6/Microsoft.PowerShell.Core/About/about_Switch.md +++ b/reference/7.6/Microsoft.PowerShell.Core/About/about_Switch.md @@ -1,7 +1,7 @@ --- description: Explains how to use a switch to handle multiple `if` statements. Locale: en-US -ms.date: 05/15/2025 +ms.date: 05/19/2025 online version: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_switch?view=powershell-7.6&WT.mc_id=ps-gethelp schema: 2.0.0 title: about_Switch @@ -20,11 +20,11 @@ variables and the properties of objects. To check multiple conditions, you can use a `switch` statement. The `switch` statement is similar to a series of `if` statements, but it's simpler. The -`switch` statement lists each condition and an optional action. If a condition -matches, the action is performed. +`switch` statement lists each condition and the corresponding action. If a +condition matches, the action is performed. -The `switch` statement can use the `$_` and `$switch` automatic variables. For -more information, see [about_Automatic_Variables][03]. +> [!IMPORTANT] +> The `switch` statement converts all values to strings before comparison. ## Syntax @@ -37,33 +37,33 @@ switch () { } ``` -This is similar to the following `if` statements: +The syntax of a `switch` statement is similar to the following `if` statements: ```Syntax -if ( -eq ()) {} -if ( -eq ()) {} +if ("$()") -eq ("$()") {} +if ("$()") -eq ("$()") {} ``` -The `` is single expression that's evaluated in expression -mode to return a value. - -The `` is an expression whose value is compared to the -input value. Expressions include literal values (strings or numbers), -variables, and scriptblocks that return a boolean value. For an example, see +Expressions include literal values (strings or numbers), variables, and +scriptblocks that return a boolean value. The `switch` statement converts all +values to strings before comparison. For an example, see [Impact of string conversion][02] later in this article. -It's important to understand that the `` value is on the -left-hand side of the comparison expression. That means the result of the -`` is on the right-hand side, which can be converted to the -type of the left-hand side value for comparison. For more information, see -[about_Comparison_Operators][05] +The `` is evaluated in expression mode. If the expression +returns more than one value, such as an array or other enumerable type, the +`switch` statement evaluates each enumerated value separately. + +The `` is an expression must resolve to a single value. +That value is compared to the input value. The value `default` is reserved for the action used when there are no other matches. -The `$_` automatic variable contains the value of the expression passed to the -`switch` statement and is available for evaluation and use within the scope of -the `` statements. +The `switch` statement can use the `$_` and `$switch` automatic variables. The +automatic variable contains the value of the expression passed to the `switch` +statement and is available for evaluation and use within the scope of the +`` statements. For more information, see +[about_Automatic_Variables][03]. The complete `switch` statement syntax is as follows: @@ -109,7 +109,8 @@ conditions. It's equivalent to an `else` clause in an `if` statement. Only one file is read a line at a time and evaluated by the `switch` statement. By default, the comparison is case-insensitive. The **File** parameter only supports one file. If multiple **File** parameters are included, only the - last one is used. For more information see [File parameter examples][01]. + last one is used. For more information see the + [**File** parameter examples][01]. - **Regex** - Performs regular expression matching of the value to the condition. If the match clause isn't a string, this parameter is ignored. The comparison is case-insensitive. The `$Matches` automatic variable is @@ -121,7 +122,11 @@ conditions. It's equivalent to an `else` clause in an `if` statement. Only one > ignored. Multiple instances of parameters are also permitted. However, only > the last parameter listed is used. -## Simple match examples +## Examples + +The following examples demonstrate the use of the `switch` statement. + +### Simple match examples In the following example, the `switch` statement compares the test value `3` to each of the conditions. When the test value matches the condition, the action @@ -159,69 +164,44 @@ It's three. Three again. ``` -To direct the `switch` to stop comparing after a match, use the `break` -statement. The `break` statement terminates the `switch` statement. - -```powershell -switch (3) { - 1 { "It's one." } - 2 { "It's two." } - 3 { "It's three."; break } - 4 { "It's four." } - 3 { "Three again." } -} -``` - -```Output -It's three. -``` +### Use `break` and `continue` to control the flow -If the test value is a collection, such as an array, each item in the -collection is evaluated in the order in which it appears. The following -examples evaluates 4 and then 2. +If the value matches multiple conditions, the action for each condition is +executed. To change this behavior, use the `break` or `continue` keywords. -```powershell -switch (4, 2) { - 1 { "It's one." } - 2 { "It's two." } - 3 { "It's three." } - 4 { "It's four." } - 3 { "Three again." } -} -``` +The `break` keyword stops processing and exits the `switch` statement. -```Output -It's four. -It's two. -``` +The `continue` keyword stops processing the current value, but continues +processing any subsequent values. -Any `break` statements apply to the collection, not to each value, as shown -in the following example. The `switch` statement is terminated by the `break` -statement in the condition of value 4. +The following example processes an array of numbers and displays if they're +odd or even. Negative numbers are skipped with the `continue` keyword. If a +non-number is encountered, execution is terminated with the `break` keyword. ```powershell -switch (4, 2) { - 1 { "It's one."; break } - 2 { "It's two." ; break } - 3 { "It's three." ; break } - 4 { "It's four." ; break } - 3 { "Three again." } +switch (1,4,-1,3,"Hello",2,1) { + {$_ -lt 0} { continue } + {$_ -isnot [int32]} { break } + {$_ % 2} { "$_ is Odd" } + {-not ($_ % 2)} { "$_ is Even" } } ``` ```Output -It's four. +1 is Odd +4 is Even +3 is Odd ``` -## Impact of string conversion +### Impact of string conversion -Any unquoted value that's not recognized as a number is treated as a string. To -avoid unintended string conversion, use script blocks to evaluate the switch -value. +All values, both input and the comparison value are converted to strings for +comparison. To avoid unintended string conversion, use script blocks to +evaluate the switch value. ```powershell switch ( ([datetime]'1 Jan 1970').DayOfWeek ) { - 4 { 'The value matches a Thursday.' } + 4 { 'The integer value matches a Thursday.' } "4" { 'The numeric string matches a Thursday.' } "Thursday" { 'The string value matches a Thursday.' } { 4 -eq $_ } { 'The expression matches a Thursday.' } @@ -242,7 +222,7 @@ statement. ```powershell if (4 -eq ([datetime]'1 Jan 1970').DayOfWeek) { - 'The value matches a Thursday.' + 'The integer value matches a Thursday.' } ``` @@ -250,40 +230,26 @@ if (4 -eq ([datetime]'1 Jan 1970').DayOfWeek) { The value matches a Thursday. ``` -## More complex match examples - -In this example, the `switch` statement is testing for the type of the value in -the hashtable. You must use and expression that returns a boolean value to -select the scriptblock to execute. +In this example, a hashtable is passed to the `switch` statement. The `switch` +converts the hashtable to a string. ```powershell -$var = @{A = 10; B = 'abc'} - -foreach ($key in $var.Keys) { - switch ($var[$key].GetType()) { - { $_ -eq [int32] } { "$key + 10 = $($var[$key] + 10)" } - { $_ -eq [string] } { "$key = $($var[$key])" } - } +$test = @{ + Test = 'test' + Test2 = 'test2' } + +$test.ToString() ``` ```Output -A + 10 = 20 -B = abc +System.Collections.Hashtable ``` -In this example, an object that's not a string or numerical data is passed to -the `switch`. The `switch` performs a string coercion on the object and -evaluates the outcome. +Notice that the string representation of the hashtable is not the same as the +value of the **Test** key. ```powershell -$test = @{ - Test = 'test' - Test2 = 'test2' -} - -$test.ToString() - switch -Exact ($test) { 'System.Collections.Hashtable' { 'Hashtable string coercion' } 'test' { 'Hashtable value' } @@ -291,10 +257,34 @@ switch -Exact ($test) { ``` ```Output -System.Collections.Hashtable Hashtable string coercion ``` +### Use `switch` to test the values in a hashtable + +In this example, the `switch` statement is testing for the type of the value in +the hashtable. We must enumerate the items in the hashtable before we can test +the values. To avoid the complications of string conversion, use a script block +that returns a boolean value to select the action scriptblock to execute. + +```powershell +$var = @{A = 10; B = 'abc'} + +foreach ($key in $var.Keys) { + switch ($var[$key].GetType()) { + { $_ -eq [int32] } { "$key + 10 = $($var[$key] + 10)" } + { $_ -eq [string] } { "$key = $($var[$key])" } + } +} +``` + +```Output +A + 10 = 20 +B = abc +``` + +### Use wildcards with `switch` + In this example, there is no matching case so there is no output. ```powershell @@ -342,6 +332,8 @@ switch -Wildcard ("fourteen") { That's too many. ``` +### Use regular expressions with `switch` + The following example uses the `-Regex` parameter. ```powershell @@ -400,34 +392,7 @@ switch ((Get-Date 1-Jan-2022), (Get-Date 25-Dec-2021)) { } ``` -If the value matches multiple conditions, the action for each condition is -executed. To change this behavior, use the `break` or `continue` keywords. - -The `break` keyword stops processing and exits the `switch` statement. - -The `continue` keyword stops processing the current value, but continues -processing any subsequent values. - -The following example processes an array of numbers and displays if they're -odd or even. Negative numbers are skipped with the `continue` keyword. If a -non-number is encountered, execution is terminated with the `break` keyword. - -```powershell -switch (1,4,-1,3,"Hello",2,1) { - {$_ -lt 0} { continue } - {$_ -isnot [int32]} { break } - {$_ % 2} { "$_ is Odd" } - {-not ($_ % 2)} { "$_ is Even" } -} -``` - -```Output -1 is Odd -4 is Even -3 is Odd -``` - -## File parameter examples +### Read the content of a file with `switch` Using the `switch` statement with the **File** parameter is an efficient way to process large files line by line. PowerShell streams the lines of the file to @@ -450,10 +415,10 @@ switch -Regex -File .\README.md { } ``` -The `` argument is interpreted as a wildcard expression, but it must -match only one file. The following example is the same as the previous one -except it uses a wildcard in the `` argument. This example works -because the wildcard pattern matches only one file. +The `` argument accepts wildcard expressions, but it must match only +one file. The following example is the same as the previous one except it uses +a wildcard in the `` argument. This example works because the +wildcard pattern matches only one file. ```powershell switch -Regex -File .\README.* { @@ -478,16 +443,15 @@ switch -File $fileEscaped { foo { 'Foo' } } ## See also - [about_Break][04] -- [about_Continue][06] -- [about_If][07] -- [about_Script_Blocks][08] +- [about_Continue][05] +- [about_If][06] +- [about_Script_Blocks][07] - -[01]: #file-parameter-examples + +[01]: #read-the-content-of-a-file-with-switch [02]: #impact-of-string-conversion [03]: about_Automatic_Variables.md [04]: about_break.md -[05]: about_Comparison_Operators.md -[06]: about_Continue.md -[07]: about_If.md -[08]: about_Script_Blocks.md +[05]: about_Continue.md +[06]: about_If.md +[07]: about_Script_Blocks.md diff --git a/reference/docs-conceptual/dev-cross-plat/performance/parallel-execution.md b/reference/docs-conceptual/dev-cross-plat/performance/parallel-execution.md new file mode 100644 index 000000000000..83eb50737d34 --- /dev/null +++ b/reference/docs-conceptual/dev-cross-plat/performance/parallel-execution.md @@ -0,0 +1,281 @@ +--- +description: The article discusses the performance characteristics of parallel execution to help you choose the best approach for your code. +ms.date: 05/15/2025 +title: Optimize performance using parallel execution +--- +# Optimize performance using parallel execution + +PowerShell provides several options for the creation of parallel invocations. + +- `Start-Job` runs each job in a separate process, each with a new instance of PowerShell. In many + cases, a linear loop is faster. Also, serialization and deserialization can limit the usefulness + of the objects returned. This command is built in to all versions of PowerShell. +- `Start-ThreadJob` is a cmdlet found in the **ThreadJob** module. This command uses PowerShell + runspaces to create and manage thread-based jobs. These jobs are lighter-weight than the jobs + created by `Start-Job` and avoid potential loss of type fidelity required by cross-process + serialization and deserialization. The **ThreadJob** module comes with PowerShell 7 and higher. + For Windows PowerShell 5.1, you can install this module from the PowerShell Gallery. +- Use the **System.Management.Automation.Runspaces** namespace from the PowerShell SDK to create + your own parallel logic. Both `ForEach-Object -Parallel` and `Start-ThreadJob` use PowerShell + runspaces to execute the code in parallel. +- Workflows are a feature of Windows PowerShell 5.1. Workflows aren't available in PowerShell 7.0 + and higher. Workflows are a special type of PowerShell script that can run in parallel. They're + designed for long-running tasks and can be paused and resumed. Workflows aren't recommended for + new development. For more information, see [about_Workflows][02]. +- `ForEach-Object -Parallel` is a feature of PowerShell 7.0 and higher. Like `Start-ThreadJob`, it + uses PowerShell runspaces to create and manage thread-based jobs. This command is designed for use + in a pipeline. + +## Limit execution concurrency + +Running scripts in parallel doesn't guarantee improved performance. For example, the following +scenarios can benefit from parallel execution: + +- Compute intensive scripts on multi-threaded multi-core processors +- Scripts that spend time waiting for results or doing file operations, as long as those operations + don't block each other. + +It's important to balance the overhead of parallel execution with the type of work done. Also, there +are limits to the number of invocations that can run in parallel. + +The `Start-ThreadJob` and `ForEach-Object -Parallel` commands have a **ThrottleLimit** parameter to +limit the number of jobs running at one time. As more jobs are started, they're queued and wait +until the current number of jobs drops below the throttle limit. As of PowerShell 7.1, +`ForEach-Object -Parallel` reuses runspaces from a runspace pool by default. The **ThrottleLimit** +parameter sets the runspace pool size. The default runspace pool size is 5. You can still create a +new runspace for each iteration using the UseNewRunspace switch. + +The `Start-Job` command doesn't have a **ThrottleLimit** parameter. You have to manage the number of +jobs running at one time. + +## Measure performance + +The following function, `Measure-Parallel`, compares the speed of the following parallel execution +approaches: + +- `Start-Job` - creates a child PowerShell process behind the scenes +- `Start-ThreadJob` - runs each job in a separate thread +- `ForEach-Object -Parallel` - runs each job in a separate thread +- `Start-Process` - invokes an external program asynchronously + + > [!NOTE] + > This approach only makes sense if your parallel tasks only consist of a single call to an + > external program, as opposed to running a block of PowerShell code. Also, the only way to + > capture output with this approach is by redirecting to a file. + +```powershell +function Measure-Parallel { + [CmdletBinding()] + param( + [ValidateRange(2, 2147483647)] + [int] $BatchSize = 5, + + [ValidateSet('Job', 'ThreadJob', 'Process', 'ForEachParallel', 'All')] + [string[]] $Approach, + + # pass a higher count to run multiple batches + [ValidateRange(2, 2147483647)] + [int] $JobCount = $BatchSize + ) + + $noForEachParallel = $PSVersionTable.PSVersion.Major -lt 7 + $noStartThreadJob = -not (Get-Command -ErrorAction Ignore Start-ThreadJob) + + # Translate the approach arguments into their corresponding hashtable keys (see below). + if ('All' -eq $Approach) { $Approach = 'Job', 'ThreadJob', 'Process', 'ForEachParallel' } + $approaches = $Approach.ForEach({ + if ($_ -eq 'ForEachParallel') { 'ForEach-Object -Parallel' } + else { $_ -replace '^', 'Start-' } + }) + + if ($noStartThreadJob) { + if ($interactive -or $approaches -contains 'Start-ThreadJob') { + Write-Warning "Start-ThreadJob is not installed, omitting its test." + $approaches = $approaches.Where({ $_ -ne 'Start-ThreadJob' }) + } + } + if ($noForEachParallel) { + if ($interactive -or $approaches -contains 'ForEach-Object -Parallel') { + Write-Warning 'ForEach-Object -Parallel require PowerShell v7+, omitting its test.' + $approaches = $approaches.Where({ $_ -ne 'ForEach-Object -Parallel' }) + } + } + + # Simulated input: Create 'f0.zip', 'f1'.zip', ... file names. + $zipFiles = 0..($JobCount - 1) -replace '^', 'f' -replace '$', '.zip' + + # Sample executables to run - here, the native shell is called to simply + # echo the argument given. + $exe = if ($env:OS -eq 'Windows_NT') { 'cmd.exe' } else { 'sh' } + + # The list of its arguments *as a single string* - use '{0}' as the placeholder + # for where the input object should go. + $exeArgList = if ($env:OS -eq 'Windows_NT') { + '/c "echo {0} > NUL:"' + } else { + '-c "echo {0} > /dev/null"' + } + + # A hashtable with script blocks that implement the 3 approaches to parallelism. + $approachImpl = [ordered] @{} + + # child-process-based job + $approachImpl['Start-Job'] = { + param([array] $batch) + $batch | + ForEach-Object { + Start-Job { + Invoke-Expression ($using:exe + ' ' + ($using:exeArgList -f $args[0])) + } -ArgumentList $_ + } | + Receive-Job -Wait -AutoRemoveJob | Out-Null + } + + # thread-based job - requires the ThreadJob module + if (-not $noStartThreadJob) { + # If Start-ThreadJob is available, add an approach for it. + $approachImpl['Start-ThreadJob'] = { + param([array] $batch) + $batch | + ForEach-Object { + Start-ThreadJob -ThrottleLimit $BatchSize { + Invoke-Expression ($using:exe + ' ' + ($using:exeArgList -f $args[0])) + } -ArgumentList $_ + } | + Receive-Job -Wait -AutoRemoveJob | Out-Null + } + } + + # ForEach-Object -Parallel job + if (-not $noForEachParallel) { + $approachImpl['ForEach-Object -Parallel'] = { + param([array] $batch) + $batch | ForEach-Object -ThrottleLimit $BatchSize -Parallel { + Invoke-Expression ($using:exe + ' ' + ($using:exeArgList -f $_)) + } + } + } + + # direct execution of an external program + $approachImpl['Start-Process'] = { + param([array] $batch) + $batch | + ForEach-Object { + Start-Process -NoNewWindow -PassThru $exe -ArgumentList ($exeArgList -f $_) + } | + Wait-Process + } + + # Partition the array of all indices into subarrays (batches) + $batches = @( + 0..([math]::Ceiling($zipFiles.Count / $batchSize) - 1) | ForEach-Object { + , $zipFiles[($_ * $batchSize)..($_ * $batchSize + $batchSize - 1)] + } + ) + + $tsTotals = foreach ($appr in $approaches) { + $i = 0 + $tsTotal = [timespan] 0 + $batches | ForEach-Object { + Write-Verbose "$batchSize-element '$appr' batch" + $ts = Measure-Command { & $approachImpl[$appr] $_ | Out-Null } + $tsTotal += $ts + if (++$i -eq $batches.Count) { + # last batch processed. + if ($batches.Count -gt 1) { + Write-Verbose ("'$appr' processing $JobCount items finished in " + + "$($tsTotal.TotalSeconds.ToString('N2')) secs.") + } + $tsTotal # output the overall timing for this approach + } + } + } + + # Output a result object with the overall timings. + $oht = [ordered] @{} + $oht['JobCount'] = $JobCount + $oht['BatchSize'] = $BatchSize + $oht['BatchCount'] = $batches.Count + $i = 0 + foreach ($appr in $approaches) { + $oht[($appr + ' (secs.)')] = $tsTotals[$i++].TotalSeconds.ToString('N2') + } + [pscustomobject] $oht +} +``` + +The following example uses `Measure-Parallel` to run 20 jobs in parallel, 5 at a time, using all +available approaches. + +```powershell +Measure-Parallel -Approach All -BatchSize 5 -JobCount 20 -Verbose +``` + +The following output comes from a Windows computer running PowerShell 7.5.1. Your timing can vary +based on many factors, but the ratios should provide a sense of relative performance. + +```Output +VERBOSE: 5-element 'Start-Job' batch +VERBOSE: 5-element 'Start-Job' batch +VERBOSE: 5-element 'Start-Job' batch +VERBOSE: 5-element 'Start-Job' batch +VERBOSE: 'Start-Job' processing 20 items finished in 7.58 secs. +VERBOSE: 5-element 'Start-ThreadJob' batch +VERBOSE: 5-element 'Start-ThreadJob' batch +VERBOSE: 5-element 'Start-ThreadJob' batch +VERBOSE: 5-element 'Start-ThreadJob' batch +VERBOSE: 'Start-ThreadJob' processing 20 items finished in 2.37 secs. +VERBOSE: 5-element 'Start-Process' batch +VERBOSE: 5-element 'Start-Process' batch +VERBOSE: 5-element 'Start-Process' batch +VERBOSE: 5-element 'Start-Process' batch +VERBOSE: 'Start-Process' processing 20 items finished in 0.26 secs. +VERBOSE: 5-element 'ForEach-Object -Parallel' batch +VERBOSE: 5-element 'ForEach-Object -Parallel' batch +VERBOSE: 5-element 'ForEach-Object -Parallel' batch +VERBOSE: 5-element 'ForEach-Object -Parallel' batch +VERBOSE: 'ForEach-Object -Parallel' processing 20 items finished in 0.79 secs. + +JobCount : 20 +BatchSize : 5 +BatchCount : 4 +Start-Job (secs.) : 7.58 +Start-ThreadJob (secs.) : 2.37 +Start-Process (secs.) : 0.26 +ForEach-Object -Parallel (secs.) : 0.79 +``` + +Conclusions + +- The `Start-Process` approach performs best because it doesn't have the overhead of job management. + However, as previously noted, this approach has fundamental limitations. +- The `ForEach-Object -Parallel` adds the least overhead, followed by `Start-ThreadJob`. +- `Start-Job` has the most overhead because of the hidden PowerShell instances it creates for each + job. + +## Acknowledgments + +Much of the information is this article is based on the answers from [Santiago Squarzon][04] and +[mklement0][05] in this [Stack Overflow post][03]. + +You may also be interested in the [PSParallelPipeline][06] module created by Santiago Squarzon. + +## Further reading + +- [Start-Job][08] +- [about_Jobs][01] +- [Start-ThreadJob][10] +- [ForEach-Object][07] +- [Start-Process][09] + + +[01]: /powershell/module/microsoft.powershell.core/about/about_jobs +[02]: /powershell/module/psworkflow/about/about_workflows +[03]: https://stackoverflow.com/questions/73997250/powershell-test-the-performance-efficiency-of-asynchronous-tasks-with-start-job/ +[04]: https://stackoverflow.com/users/15339544/santiago-squarzon +[05]: https://stackoverflow.com/users/45375/mklement0 +[06]: https://www.powershellgallery.com/packages/PSParallelPipeline/ +[07]: xref:Microsoft.PowerShell.Core.ForEach-Object +[08]: xref:Microsoft.PowerShell.Core.Start-Job +[09]: xref:Microsoft.PowerShell.Management.Start-Process +[10]: xref:ThreadJob.Start-ThreadJob diff --git a/reference/docs-conceptual/toc.yml b/reference/docs-conceptual/toc.yml index 438e00c09c16..b789e30deca4 100644 --- a/reference/docs-conceptual/toc.yml +++ b/reference/docs-conceptual/toc.yml @@ -397,6 +397,8 @@ items: href: dev-cross-plat/performance/script-authoring-considerations.md - name: Module performance considerations href: dev-cross-plat/performance/module-authoring-considerations.md + - name: Optimize performance using parallel execution + href: dev-cross-plat/performance/parallel-execution.md - name: Developing modern modules items: - name: Writing portable modules diff --git a/reference/module/index.md b/reference/module/index.md index 6345464ef6de..9509070a6afd 100644 --- a/reference/module/index.md +++ b/reference/module/index.md @@ -12,7 +12,7 @@ ms.manager: sewhee ms.product: powershell ms.topic: landing-page quickFilterColumn1: powershell-7.4,windowsserver2025-ps -quickFilterColumn2: azps-13.5.0,sqlserver-ps +quickFilterColumn2: azps-14.0.0,sqlserver-ps quickFilterColumn3: graph-powershell-1.0,systemcenter-ps-2022 title: PowerShell Module Browser ---