Skip to content

Commit

Permalink
main
Browse files Browse the repository at this point in the history
  • Loading branch information
sharpchen committed Jan 3, 2025
1 parent 9622452 commit 0428e83
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 53 deletions.
24 changes: 12 additions & 12 deletions docs/document/PowerShell/docs/File System/4.Manipulate Item.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# Manipulate Item

## Create Item

Powershell uses a single cmdlet named `New-Item` to represent all kinds of creation logic for file system items like folder, file, symlinks...

> [!TIP]
> Use `ni` alias for `New-Item`.
## File
### File

`New-Item` creates file without extra options by default.

Expand All @@ -25,7 +26,7 @@ New-Item <file_path>
> [!TIP]
> Use `-Force` flag to overwrite existing target.
### Directory
#### Directory

```ps1
New-Item <dir_path> -ItemType Directory
Expand All @@ -42,7 +43,7 @@ mkdir <dir_path>
> [!TIP]
> Use `-Force` flag to overwrite existing target.
### Symbolic Link
#### Symbolic Link

```ps1
New-Item <symlink_path> -Target <source> -ItemType SymbolicLink
Expand All @@ -54,26 +55,25 @@ New-Item <symlink_path> -Target <source> -ItemType SymbolicLink
> [!TIP]
> Use `-Force` flag to overwrite existing target.
### Ignore Wildcards

`-Path` translates wildcards by default, if you do need to include special characters from wildcards syntax for your new item, use `-LiteralPath`.

```ps1
New-Item -LiteralPath 'foo*.txt' # creates a file literally named `foo*.txt`
```

## Delete

- delete file

```ps1
ri <file>
gci -file | ri
```

- delete folder

```ps1
ri -rec -force <folder>
gci -dir | ri -rec -force
```

### Rename
## Rename

```ps1
rni <item> -NewName { $_.Name -replace 'foo', 'bar' }
gci -file | rni -NewName { $_.Name -replace 'foo', 'bar' }
```
4 changes: 2 additions & 2 deletions docs/document/PowerShell/docs/History.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
- Session history

```ps1
h | Select-String <text>
h | sls <text>
```

- All history

```ps1
gc (Get-PSReadLineOption).HistorySavePath | Select-String <text>
gc (Get-PSReadLineOption).HistorySavePath | sls <text>
```
13 changes: 13 additions & 0 deletions docs/document/PowerShell/docs/Job/Job Manipulation.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,16 @@ The return type of job result is not certain. It depends on the action you speci
- `-Timeout`: wait until timeout were reached
- `-Force`: wait for `Suspended` and `Disconnected`
### `Wait-Job` or `Receive-Job -Wait`
You may be confused about `Wait-Job` and `Receive-Job -Wait`; They both wait for jobs, but `Wait-Job` respects the order of how jobs were created while `Receive-Job` only collects by order of completion time.
`Wait-Job` waits for all child jobs to complete without collecting anything, so the order remains, finally `Receive-Job` retrieve the result by that order.
Direct `Receive-Job` collects immediately when each job is completed, so the order is random.
```ps1
# this is always sorted
1..10 | foreach -Parallel { echo $_ } -AsJob | wjb | rcjb # 1 2 3 4 5 .. 10
# this has random order
1..10 | foreach -Parallel { echo $_ } -AsJob | rcjb -Wait
```
5 changes: 5 additions & 0 deletions docs/document/PowerShell/docs/Job/Thread Job.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ $job.GetType().Name # ThreadJob
```ps1
1..10 | foreach -Parallel { echo $_ } -AsJob | rcjb -Wait
```

## Thread Safety

Do not mutate or access member of a shared variable in thread jobs unless you use thread-safe types!

200 changes: 163 additions & 37 deletions docs/document/PowerShell/docs/Language/Function.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,5 @@
# Function

## Return

Powershell allows implicit return, and multiple implicit returns.

> [!NOTE]
> Implicit returns are auto-collected as an array or single value.
> And it does not print out anything.
```ps1
[int] function Sum {
param([int]$l, [int]$r)
$l + $r # implicit return # [!code highlight]
}
# You won't need to declare an array and append it on each loop!
# they're collected automatically as they're implicit returns
function Foo {
for($i = 0; $i -lt 10; $i = $i + 1) {
$i
}
}
(Foo).GetType().Name # object[] # [!code highlight]
```

Explicit return is surely supported, but more like a necessity to exit inside a flow.

```ps1
[int] function Sum {
param([int]$l, [int]$r)
return $l + $r # explicit return # [!code highlight]
$r + $l # not reachable # [!code warning]
}
```

## Parameter

Expand Down Expand Up @@ -199,11 +165,38 @@ Will throw an error if any parameter does not satisfies the condition.
param (
[ValidateScript({ ($_ % 2) -ne 0 })]
[int[]]$Odd
[ValidateScript({ $_.Length < 5 })]
[ValidateScript({ $_.Length < 5 }, ErrorMessage = "{0} is not valid")] # 0 is the input value
[string]$Name
)
```

- `ValidateLength(min, max)`
- `ValidateCount(min, max)`
- `AllowNull()`
- `AllowEmptyString()`
- `AllowEmptyCollection()`
- `ValidatePattern(regex)`
- `ValidateRange(min, max)`
- or any value of enum `ValidateRangeKind`: `Positive`, `Negative`, `NonNegative`, `NonPositive`
```ps1
param(
[ValidateRange("Positive")]
[int]$Number
)
```
- `ValidateSet(foo, bar, ...)`: provides completion for predefined entries
- `ValidateNotNull()`
- `ValidateNotNullOr()`
- `ValidateNotNullOrEmpty()`: not a empty string or collection or null
- `ValidateNotNullOrWhiteSpace()`
- `ValidateDrive(drive, ...)`: check whether specified path is valid from certain PSDrive
```ps1
param(
[ValidateDrive("C", "D")]
[string]$Path
)
```
### Pass By Reference
Parameter passed by reference is implemented by a wrapper `System.Management.Automation.PSReference`.
Expand Down Expand Up @@ -279,6 +272,139 @@ function Foo {
}
```
## Lifetime
## Mimicking Cmdlet

A function would generally not acting a cmdlet unless it was annotated with `CmdletBinding()` attribute.

`CmdletBinding()` can have the following properties:

- `DefaultParameterSetName`: name of implicit Parameter Set
- `HelpURI`: link to documenetation
- `SupportsPaging`: implicitly adds parameters `-First`, `-Skip`, `-IncludeTotalCount`, value accessible by `$PSCmdlet.PagingParameters`
```ps1
function foo {
[CmdletBinding(SupportsPaging)]
param()
$PSCmdlet.PagingParameters.Skip
$PSCmdlet.PagingParameters.First
$PSCmdlet.PagingParameters.IncludeTotalCount
}
```
- `SupportsShouldProcess`: implicitly adds `-Confirm` and `-WhatIf`
- `ConfirmImpact`: specify impact of `-Confirm`
- `PositionalBinding`:
<!-- TODO: complete description for ConfirmImpact and PositionalBinding -->
## Parameter Set
How a same cmdlet manage different syntax for different usages? The trick is **Parameter Set**.
Parameter Set is a classification on paramater to distinguish or limit the use of parameters from scenarios.
- a parameter set must have at least one unique parameter to others to identify the set
- a parameter can have multiple Parameter Set
- a parameter can have different roles in different Parameter Set, can be mandatory in one and optional in another
- a parameter without explicit Parameter Set belongs to all other Parameter Set
- at least one parameter in the Parameter Set is mandatory
- only one parameter in set can accept `ValueFromPipeline`
### Parameter Set Idetifier at Runtime
`$PSCmdlet.ParameterSetName` reflects the Parameter Set been chosen when a cmdlet is executing with certain syntax.
## Common Parameters
Any function or cmdlet applied with `CmdletBinding()` or `Parameter()` attribute has the following implicit parameters added by PowerShell:
- ErrorAction (ea): specify action on error
- ErrorVariable (ev): declare **inline** and store the error on the variable instead of `$Error`. Use `-ErrorVariable +var` to append error to the variable
```ps1
gcm foo -ev bar # inline declaration for $bar
$bar # contains error
gcm baz -ev +bar
$bar # contains two errors
```
> [!NOTE]
> The inline variable is an `ArrayList`
- InformationAction (infa): similar to `ea`
- InformationVariable (iv): similar to `ev`
- WarningAction (wa): similar to `ea`
- WarningVariable (wv): similar to `ev`
- ProgressAction (proga)
- OutVariable (ov): declare **inline** and store the output to the variable. Similar to `ev`.
It's interesting that `-OutVariable` collects incremnentally.
It collects new item from pipeline on iteration.
```ps1
1..5 | % { $_ } -OutVariable foo | % { "I am $foo" }
# I am 1
# I am 1,2
# I am 1,2,3
# I am 1,2,3,4
# I am 1,2,3,4,5
```
- Debug (db): print verbose debug message, overrides `$DebugPreference`
- OutBuffer (ob)
- PipelineVariable (pv) <!-- TODO: PipelineVariable -->
- Verbose (vb): whether display the verbose message from `Write-Verbose`
## Mitigation Parameters
- WhatIf (wi)
- Confirm (cf)
## Return
Powershell allows implicit return, and multiple implicit returns.
> [!NOTE]
> Implicit returns are auto-collected as an array or single value.
> And it does not print out anything.
```ps1
function Sum {
param([int]$l, [int]$r)
$l + $r # implicit return # [!code highlight]
}
# You won't need to declare an array and append it on each loop!
# they're collected automatically as they're implicit returns
function Foo {
for($i = 0; $i -lt 10; $i = $i + 1) {
$i
}
}
(Foo).GetType().Name # object[] # [!code highlight]
```

Explicit return is surely supported, but more like a necessity to exit inside a flow.

- Function should be define before it was called.
```ps1
function Sum {
param([int]$l, [int]$r)
return $l + $r # explicit return # [!code highlight]
$r + $l # not reachable # [!code warning]
}
```

### Output Type

`OutputTypeAttribute(type: System.Type | System.String, parameterSet?: System.String)` matters when you need auto generated help for the function you write.
A function can have different return types for different parameter sets.
PowerShell never do type checking by this attribute, it's just responsible for help generation, write with carefulness!

```ps1
[CmdletBinding(DefaultParameterSetName = 'ID')]
[OutputType('System.Int32', ParameterSetName = 'ID')]
[OutputType([string], ParameterSetName = 'Name')]
param (
[Parameter(Mandatory = $true, ParameterSetName = 'ID')]
[int[]]
$UserID,
[Parameter(Mandatory = $true, ParameterSetName = 'Name')]
[string[]]
$UserName
)
```
Loading

0 comments on commit 0428e83

Please sign in to comment.