diff --git a/docs/document/Powershell/docs/1.Overview.md b/docs/document/Powershell/docs/1.Overview.md index ca1a8346..1f2507bf 100644 --- a/docs/document/Powershell/docs/1.Overview.md +++ b/docs/document/Powershell/docs/1.Overview.md @@ -8,5 +8,3 @@ - All language syntax, pattern syntax and even strings are case-insensitive. (There's exception for file system on non-Windows platform) - Everything is object, more than plain text in shell. - Powershell formats the object value as a table if the object is not a primitive type. For primitive types, `Tostring()` will be used instead. -- Always handle both array and singular object. - - A powershell cmdlet always accepts an array or an single object as input parameter. And returns the result as an array or an object too. diff --git a/docs/document/Powershell/docs/Language/Array.md b/docs/document/Powershell/docs/Language/Array.md index e647b708..f43651ab 100644 --- a/docs/document/Powershell/docs/Language/Array.md +++ b/docs/document/Powershell/docs/Language/Array.md @@ -15,8 +15,10 @@ $foo = @() # empty array > [!NOTE] > The default type of a array literal is `object[]`, you can annotate with a type. +> To create an empty array with fixed size, invoke `new()` constructor, this is also strongly typed. > ```ps1 > [int[]]$foo = 1, 2, 3 +> $bar = [int[]]::new(5) > ``` > [!TIP] @@ -28,6 +30,8 @@ $foo = @() # empty array >```ps1 >(1,2,3).Length # 3 > +>$(1,2,3).Length # 3 this has different semantic but with same result +> >(,(1,2,3)).Length # 3 # does spread items # [!code highlight] > >(,@(1,2,3)).Length # 1 # does not spread items # [!code highlight] @@ -35,10 +39,10 @@ $foo = @() # empty array >((gci), (gci ..)).Length # 2 # [!code highlight] > ``` -### Collect from Expressions +### From Expression and Statement -`@()` actually collects from expressions, and all items from expressions will be flattened into a whole array. -So it can be another way to spread out arrays into one. +`@()` collects from expressions or statements like control flows that implicitly returns. +You can choose either flatten items from expressions or treat them as a sub-array. - Use `;` to separate expressions for flatten arrays from them. - Use `,` to separate expressions if you want them to be sub-array. @@ -49,25 +53,54 @@ So it can be another way to spread out arrays into one. @((ls), (ls ..)).Length # 2 @(ls; ls ..).Length # > 0 + +@( + if ($true) { + 'yield this value to the array' + 'yield this value again' + } +).Length # 2 ``` -## Access an Item +### From Enumerator -Powershell allows indexer syntax to access one or more items at a time. +Similar to expression, you can collect items from a `IEnumerator`. + +```ps1 +$foo = @{ + Name = 'foo' + Age = 18 +} + +@($foo.GetEnumerator()).Length # 2, System.Collections.DictionaryEntry from the HashTable # [!code highlight] +``` + +## Access Item + +Powershell allows indexer syntax to access one or more items at a time or use `Select-Object`. ```ps1 @(1,2,3)[0] # 1 -@(1,2,3)[0, 2] # 1, 3 +@(1,2,3) | select -index 1 # 2 +@(1,2,3)[0, 1] # 1, 2 returns an array though ``` +> [!NOTE] +> The default value of a array item is `$null`. +> Since it's a dynamic language, there's no error raised when index is out of the range. + ## Concatenation -Generates new array from two concatenated. +Generates new array from two concatenated or with new item. ```ps1 ((1,2,3) + (1,2,3)).Length # 6 +(1,2,3) + 4 # 1,2,3,4 ``` +> [!NOTE] +> Can use `+=` when you operate on a array variable. + ## Repetition Use `*` to repeat the array content for certain times. @@ -76,6 +109,12 @@ Use `*` to repeat the array content for certain times. ((1,2,3) * 3).Length # 9 ``` +A pratical usage of repetition is initialization with same value to the whole array. + +```ps1 +@(255) * 100 # Fill up array sized 100 with 255 to all elements +``` + ## Slicing Use range operator to slice an array. @@ -107,6 +146,54 @@ Separate different ranges by `+` to generate a range union. (1..10)[0..2+4..5+7] ``` +## Substration + +To substract a collection from another collection, you can certainly use `LINQ` or use a simple pipeline. + +```ps1 +@(1,2,3) | where { @(1, 2) -notcontains $_ } # 3 +``` + +## Null Checking + +Checking null for collections is a quirk in PowerShell, `$arr -eq $null` checks all items instead of the whole array. + +```ps1 +$arr = 1,2,3 + +$arr -eq $null # empty array + +$null -eq $arr # False, the result we expected # [!code highlight] + +if ($arr) { 'array is not null and not empty' } # check both empty and null +``` + +## To List + +PowerShell allows direct casting a array to an `ArrayList` or generic `List`. + +```ps1 +using namespace System.Collections.Generic + +[List[int]]@(1,2,3) + +[System.Collections.ArrayList]@(1,2,3) +``` + +## Filtering & Transformation by Keyword Operators + +Keyword operators has special functionalities on collections. +`-match`, `-notmatch`, `-replace`, `-split` handles for all items in the left operand collection, the result is always an array. + +```ps1 +# Returns items that matches the regex +@('John', 'Jane', 'Janet') -match 'Jane' # Jane, Janet. +(@('John', 'Jane', 'Janet') -notmatch 'Jane') -is [array] # True, only John matches and still an array. + +@('John', 'Jane', 'Janet') -replace 'J','K' # Kohn Kane Kanet +'1,2,3','1,2,3' -split ',' # 1 2 3 1 2 3, strings +``` + ## Multi-Dim Array You'll have to create Multi-Dim array from .NET type constructor only. diff --git a/docs/document/Powershell/docs/Language/HashTable.md b/docs/document/Powershell/docs/Language/HashTable.md index ed4dd94d..668e502c 100644 --- a/docs/document/Powershell/docs/Language/HashTable.md +++ b/docs/document/Powershell/docs/Language/HashTable.md @@ -1,5 +1,66 @@ # HashTable +HashTable is a dynamicly typed data structure in PowerShell, it implements `IDictionary` but is wrapped with the extended types system. +It's the native type and is unique to PowerShell itself. + +```ps1 +@{} -is [System.Collections.IDictionary] # True +``` + +## Creation + +PowerShell has builtin syntax for creating a HashTable. +Inline declaration requires `;` to distinguish key-value pairs. + +```ps1 +$foo = @{ + Name = 'foo' + Age = 18 +} +$foo = @{ Name = 'foo'; Age = 18 } +``` + +### Ordered HashTable + +`[ordered]` is a mark can be used when creating HashTable literal, it makes sure that all entries are ordered as in declaration and subsequent appending. + +```ps1 +([ordered]@{ + C = 'C' + B = 'B' + A = 'A' +}).Keys # C B A + +@{ + C = 'C' + B = 'B' + A = 'A' +}.Keys # B C A +``` + +> [!NOTE] +> `[ordered]` can not be used as type, but it's indeed a `System.Collections.Specialized.OrderedDictionary`. +>```ps1 +>([ordered]@{}).GetType().FullName # System.Collections.Specialized.OrderedDictionary +>``` + +## Access Values + +You can access value of one or more keys by indexer. +```ps1 +$foo['Name'] # foo +$foo['Name', 'Age'] # @('foo', 18) +``` + +`.` accessor would also works **as long as there's no duplicated Extended Property with the same name of the key you passed.** + +```ps1 +$foo.Name # foo +``` + +> [!TIP] +> Always use indexer to access value of a HashTable. `.` will prefer Extended Property that might be unexpected. + ## Merging ```ps1 diff --git a/docs/document/Powershell/docs/Language/String.md b/docs/document/Powershell/docs/Language/String.md index 867c4bf0..ed815bd5 100644 --- a/docs/document/Powershell/docs/Language/String.md +++ b/docs/document/Powershell/docs/Language/String.md @@ -91,10 +91,40 @@ Use double `'` to escape `'` in a verbatim string. > [!NOTE] > See [about_Specical_Characters](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_special_characters?view=powershell-7.4) +## Arithmetic with Numerics + +Powershell will try to convert the string on the right operand to the same type as left operand. +Exception will be raised if conversion failed. + +```ps1 +1 + '2' # 3 +'2' + 1 # '21' +[DateTime]::Now + '00:00:15:00' # adds 15 minutes +``` + ## Split & Join +```ps1 +'1,2,3' -split ',' # 1 2 3 as strings +(gci -file) -join ',' # ToString is invoked to evaluated objects to string. +``` + +## Match & Replace + +```ps1 +'Janet is a girl' -match 'Jane' # True +'Janet is a girl' -replace '^Janet', 'Jane' +``` + ## Format String +Template string syntax is the same as `C#`. +Standard numeric format like `:C`, `:X` are supported. + +```ps1 +'This is a {0} string' -f 'format' +``` + ## Repetition Use `*` to repeat a string. diff --git a/docs/document/Powershell/docs/Object Manipulation/Object Members.md b/docs/document/Powershell/docs/Object Manipulation/Object Members.md index d7dc7169..14638620 100644 --- a/docs/document/Powershell/docs/Object Manipulation/Object Members.md +++ b/docs/document/Powershell/docs/Object Manipulation/Object Members.md @@ -28,9 +28,11 @@ The whole array returned from `Get-Member` is `object[]`, each item inside is a ### Inspect from Object To treat a whole collection as the object to be inspected, do not pipe it, pass it to `-InputObject` instead. +Or magically wrap it as a single-item collection. ```ps1 gm -InputObject (gci -file) # TypeName: System.Object[] +,(gci -file) | gm ``` ## Member Types diff --git a/docs/document/Powershell/docs/Type System/1.Overview.md b/docs/document/Powershell/docs/Type System/1.Overview.md new file mode 100644 index 00000000..bf195b85 --- /dev/null +++ b/docs/document/Powershell/docs/Type System/1.Overview.md @@ -0,0 +1,3 @@ +# Overview + +- Intrinsic Members diff --git a/docs/document/Powershell/docs/Understanding Pipeline.md b/docs/document/Powershell/docs/Understanding Pipeline.md index 87c3a1b4..3495b365 100644 --- a/docs/document/Powershell/docs/Understanding Pipeline.md +++ b/docs/document/Powershell/docs/Understanding Pipeline.md @@ -10,7 +10,7 @@ Overview of pipeline in powershell: -## How Cmdlet Accept Pipeline Input +## Pipeline Input Strategy There's two solution when a pipeline input comes in as a fallback: @@ -33,7 +33,7 @@ gci -File | spps ByValue is always tried first, and then use ByPropertyName, or it finally throws. A parameter accepts pipeline input does not necessarily have both solutions, it can have at least one of them. -## How PowerShell Enumerate Pipeline Input +## Pipeline Input as Enumerator As we know, PowerShell can handle multiple objects from an enumerator from object that implements `IEnumerable` or `IEnumerable`, or even duck typed with `GetEnumerator`. @@ -43,20 +43,22 @@ While for types that are not linear collection, manually invoking `GetEnumerator - HashTable has dynamic typing so we can't presume a uniformed calculation for our cmdlet - `string` is `IEnumerable` but we surely don't expect the auto enumeration. +This is simply because these types are more likely to be treated as a whole object, even when dictionaries are `IEnumerable>`. + ```ps1 $table = @{ Name = 'foo'; Age = 18 } ($table | measure).Count # 1 ($table.GetEnumerator() | measure).Count # 2 # [!code highlight] ``` -This is simply because these types are more likely to be treated as a whole object, even when dictionaries are `IEnumerable>`. - - ## Enumerate Pipeline Items -You can use `$input` to refer to the enumerator passed to the function. This is another way to access pipeline input items but with more control. +You can use `$input` to refer to the enumerator passed to the function. This is one way to access pipeline input items but with more control. Another option is use `$_` to represent the current item in `process` block, this is way more limited but commonly used. +> [!NOTE] +> `$_` and `$input` are isolated, they don't affects each other. + - `$input` represents a enumerator for pipeline input in `process` block. - `$input` represents the whole collection for pipeline input in `end` block. - `$input` will be consumed after being used once in either `process` or `end`. Use `Reset` to get it back. @@ -64,7 +66,10 @@ Another option is use `$_` to represent the current item in `process` block, thi ### Access Current Item -`$input.Current` have to manually invoke `MoveNext` before you access `Current` in `process` block since it's not a `while` loop. +> We're not going to talk about `$_`, it's fairly simple. All the quirks is about `$input`. + +`$input.Current` is `$null` by default, you'll have to manually invoke `MoveNext` before you access `Current` in `process` block since it's not a `while` loop. + ```ps1 function Test { @@ -122,7 +127,7 @@ gci -file | Test If you write a custom function that have one or more parameters accept pipeline input, what is going on inside? - In `begin` block, there's no value assigned to each `ByPropertyName` parameter, they remain default. -- In `process` block, each +- In `process` block, each `ByPropertyName` parameter represents the current property value extracted from the current pipeline input object. ```ps1 function Foo { @@ -139,11 +144,13 @@ function Foo { } process { - $Name - $Length + $Name # Name of current pipeline item + $Length # Length of current pipeline item } } gci -file | Foo ``` +> [!TIP] +> `ByPropertyName` parameter can also be a array type, it all depends the implementation you want, it behaves the same.