diff --git a/reference/docs-conceptual/lang-spec/chapter-01.md b/reference/docs-conceptual/lang-spec/chapter-01.md
index 7b041e5fc829..3c7ac980bdeb 100644
--- a/reference/docs-conceptual/lang-spec/chapter-01.md
+++ b/reference/docs-conceptual/lang-spec/chapter-01.md
@@ -1,23 +1,11 @@
---
description: This Language Specification describe the syntax, semantics, and behavior of the PowerShell language.
-ms.date: 05/19/2021
+ms.date: 01/08/2025
title: Windows PowerShell Language Specification 3.0
---
# Windows PowerShell Language Specification 3.0
-The _Windows PowerShell Language Specification 3.0_ was published in December 2012 and is based on
-PowerShell 3.0. The specification document is available as a Microsoft Word document from the
-Microsoft Download Center at:
-[https://www.microsoft.com/download/details.aspx?id=36389](https://www.microsoft.com/download/details.aspx?id=36389)
-
-That Word document has been converted for presentation here on Microsoft Learn. During conversion,
-some editorial changes have been made to accommodate formatting for the Docs platform. Some typos
-and minor errors have been corrected.
-
-> [!IMPORTANT]
-> The contents of this documentation may not reflect the current state of PowerShell in its current
-> version. There is no plan to update this documentation to reflect the current state. This
-> documentation is presented here for historical reference.
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
## 1. Introduction
diff --git a/reference/docs-conceptual/lang-spec/chapter-02.md b/reference/docs-conceptual/lang-spec/chapter-02.md
index bb838367d1db..869a3ce53eb6 100644
--- a/reference/docs-conceptual/lang-spec/chapter-02.md
+++ b/reference/docs-conceptual/lang-spec/chapter-02.md
@@ -5,6 +5,8 @@ title: Lexical structure
---
# 2. Lexical Structure
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
## 2.1 Grammars
This specification shows the syntax of the PowerShell language using two grammars. The _lexical
diff --git a/reference/docs-conceptual/lang-spec/chapter-03.md b/reference/docs-conceptual/lang-spec/chapter-03.md
index 91b37c1a27bd..55318dc1f0de 100644
--- a/reference/docs-conceptual/lang-spec/chapter-03.md
+++ b/reference/docs-conceptual/lang-spec/chapter-03.md
@@ -5,6 +5,8 @@ title: Basic concepts
---
# 3. Basic concepts
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
## 3.1 Providers and drives
A *provider* allows access to data and components that would not otherwise be easily accessible at
diff --git a/reference/docs-conceptual/lang-spec/chapter-04.md b/reference/docs-conceptual/lang-spec/chapter-04.md
index d1f44c2b5cf0..3991ecc3f294 100644
--- a/reference/docs-conceptual/lang-spec/chapter-04.md
+++ b/reference/docs-conceptual/lang-spec/chapter-04.md
@@ -5,14 +5,16 @@ title: Types
---
# 4. Types
-In PowerShell, each value has a type, and types fall into one of two main categories: **value types**
-and **reference types**. Consider the type `int`, which is typical of value types. A value of type
-`int` is completely self-contained; all the bits needed to represent that value are stored in that
-value, and every bit pattern in that value represents a valid value for its type. Now, consider the
-array type `int[]`, which is typical of reference types. A so-called value of an array type can hold
-either a reference to an object that actually contains the array elements, or the **null reference**
-whose value is `$null`. The important distinction between the two type categories is best
-demonstrated by the differences in their semantics during assignment. For example,
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
+In PowerShell, each value has a type, and types fall into one of two main categories: **value
+types** and **reference types**. Consider the type `int`, which is typical of value types. A value
+of type `int` is completely self-contained; all the bits needed to represent that value are stored
+in that value, and every bit pattern in that value represents a valid value for its type. Now,
+consider the array type `int[]`, which is typical of reference types. A so-called value of an array
+type can hold either a reference to an object that actually contains the array elements, or the
+**null reference** whose value is `$null`. The important distinction between the two type categories
+is best demonstrated by the differences in their semantics during assignment. For example,
```powershell
$i = 100 # $i designates an int value 100
diff --git a/reference/docs-conceptual/lang-spec/chapter-05.md b/reference/docs-conceptual/lang-spec/chapter-05.md
index 2d65d60d4b3e..24fe4d6d4300 100644
--- a/reference/docs-conceptual/lang-spec/chapter-05.md
+++ b/reference/docs-conceptual/lang-spec/chapter-05.md
@@ -5,6 +5,8 @@ title: Variables
---
# 5. Variables
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
A variable represents a storage location for a value, and that value has a type. Traditional
procedural programming languages are statically typed; that is, the runtime type of a variable is
that with which it was declared at compile time. Object-oriented languages add the idea of
diff --git a/reference/docs-conceptual/lang-spec/chapter-06.md b/reference/docs-conceptual/lang-spec/chapter-06.md
index e36997f21a0d..3348a9c809f9 100644
--- a/reference/docs-conceptual/lang-spec/chapter-06.md
+++ b/reference/docs-conceptual/lang-spec/chapter-06.md
@@ -5,6 +5,8 @@ title: Conversions
---
# 6. Conversions
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
A *type conversion* is performed when a value of one type is used in a context that requires a
different type. If such a conversion happens automatically it is known as *implicit conversion*. (A
common example of this is with some operators that need to convert one or more of the values
diff --git a/reference/docs-conceptual/lang-spec/chapter-07.md b/reference/docs-conceptual/lang-spec/chapter-07.md
index 482d9929ff54..41a8dd230aeb 100644
--- a/reference/docs-conceptual/lang-spec/chapter-07.md
+++ b/reference/docs-conceptual/lang-spec/chapter-07.md
@@ -5,6 +5,8 @@ title: Expressions
---
# 7. Expressions
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
Syntax:
```Syntax
diff --git a/reference/docs-conceptual/lang-spec/chapter-08.md b/reference/docs-conceptual/lang-spec/chapter-08.md
index ff022ae22bdd..2034451860c9 100644
--- a/reference/docs-conceptual/lang-spec/chapter-08.md
+++ b/reference/docs-conceptual/lang-spec/chapter-08.md
@@ -5,6 +5,8 @@ title: Statements
---
# 8. Statements
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
## 8.1 Statement blocks and lists
Syntax:
diff --git a/reference/docs-conceptual/lang-spec/chapter-09.md b/reference/docs-conceptual/lang-spec/chapter-09.md
index ab996b5bad85..35e0ff9ec221 100644
--- a/reference/docs-conceptual/lang-spec/chapter-09.md
+++ b/reference/docs-conceptual/lang-spec/chapter-09.md
@@ -5,6 +5,8 @@ title: Arrays
---
# 9. Arrays
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
## 9.1 Introduction
PowerShell supports arrays of one or more dimensions with each dimension having zero or more
diff --git a/reference/docs-conceptual/lang-spec/chapter-10.md b/reference/docs-conceptual/lang-spec/chapter-10.md
index e3fbf948851b..bf04d3284dd8 100644
--- a/reference/docs-conceptual/lang-spec/chapter-10.md
+++ b/reference/docs-conceptual/lang-spec/chapter-10.md
@@ -5,6 +5,8 @@ title: Hashtables
---
# 10. Hashtables
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
Syntax:
> [!TIP]
diff --git a/reference/docs-conceptual/lang-spec/chapter-11.md b/reference/docs-conceptual/lang-spec/chapter-11.md
index d6a63acaad4a..a1eb3802a708 100644
--- a/reference/docs-conceptual/lang-spec/chapter-11.md
+++ b/reference/docs-conceptual/lang-spec/chapter-11.md
@@ -5,6 +5,8 @@ title: Modules
---
# 11. Modules
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
## 11.1 Introduction
As stated in [§3.14][§3.14], a module is a self-contained reusable unit that allows PowerShell code to be
diff --git a/reference/docs-conceptual/lang-spec/chapter-12.md b/reference/docs-conceptual/lang-spec/chapter-12.md
index 36dafd97f99c..13cdc3c64169 100644
--- a/reference/docs-conceptual/lang-spec/chapter-12.md
+++ b/reference/docs-conceptual/lang-spec/chapter-12.md
@@ -5,6 +5,8 @@ title: Attributes
---
# 12. Attributes
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
An *attribute* object associates predefined system information with a *target element*, which can be
a param block or a parameter ([§8.10][§8.10]). Each attribute object has an *attribute type*.
diff --git a/reference/docs-conceptual/lang-spec/chapter-13.md b/reference/docs-conceptual/lang-spec/chapter-13.md
index 8ff300d4a1ff..b5d08b24aa20 100644
--- a/reference/docs-conceptual/lang-spec/chapter-13.md
+++ b/reference/docs-conceptual/lang-spec/chapter-13.md
@@ -5,6 +5,8 @@ title: Cmdlets
---
# 13. Cmdlets
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
A cmdlet is a single-feature command that manipulates objects in PowerShell. Cmdlets can be
recognized by their name format, a verb and noun separated by a dash (`-`), such as `Get-Help`,
`Get-Process`, and `Start-Service`. A *verb pattern* is a verb expressed using wildcards, as
diff --git a/reference/docs-conceptual/lang-spec/chapter-14.md b/reference/docs-conceptual/lang-spec/chapter-14.md
index 3b6fc1f8b3b7..d9bf6163f916 100644
--- a/reference/docs-conceptual/lang-spec/chapter-14.md
+++ b/reference/docs-conceptual/lang-spec/chapter-14.md
@@ -6,6 +6,8 @@ title: Comment-Based Help
# A. Comment-Based Help
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
PowerShell provides a mechanism for programmers to document their scripts using special comment
directives. Comments using such syntax are called *help comments*. The cmdlet
[Get-Help](xref:Microsoft.PowerShell.Core.Get-Help) generates documentation from these directives.
diff --git a/reference/docs-conceptual/lang-spec/chapter-15.md b/reference/docs-conceptual/lang-spec/chapter-15.md
index 278e515b770e..b9d7d0854413 100644
--- a/reference/docs-conceptual/lang-spec/chapter-15.md
+++ b/reference/docs-conceptual/lang-spec/chapter-15.md
@@ -5,6 +5,8 @@ title: Appendix A - Grammar
---
# B. Grammar
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
This appendix contains summaries of the lexical and syntactic grammars found in the main document.
> [!TIP]
diff --git a/reference/docs-conceptual/lang-spec/chapter-16.md b/reference/docs-conceptual/lang-spec/chapter-16.md
index 88da5a8d34bc..89752423ce7e 100644
--- a/reference/docs-conceptual/lang-spec/chapter-16.md
+++ b/reference/docs-conceptual/lang-spec/chapter-16.md
@@ -5,6 +5,8 @@ title: Appendix B - References
---
# C. References
+[!INCLUDE [Disclaimer](../../includes/language-spec.md)]
+
ANSI/IEEE 754−2008, _Binary floating-point arithmetic for microprocessor systems_.
ECMA-334, _C# Language Specification_, 4th edition (June 2006),
diff --git a/reference/docs-conceptual/learn/ps101/04-pipelines.md b/reference/docs-conceptual/learn/ps101/04-pipelines.md
index d6262617af4b..1156d69ff711 100644
--- a/reference/docs-conceptual/learn/ps101/04-pipelines.md
+++ b/reference/docs-conceptual/learn/ps101/04-pipelines.md
@@ -1,38 +1,41 @@
---
description: A PowerShell one-liner is one continuous pipeline, containing multiple commands, to accomplish a single task.
ms.custom: Contributor-mikefrobbins
-ms.date: 12/08/2022
+ms.date: 01/08/2025
ms.reviewer: mirobb
title: One-liners and the pipeline
---
-# Chapter 4 - One-liners and the pipeline
-When I first started learning PowerShell, if I couldn't accomplish a task with a PowerShell
-one-liner, I went back to the GUI. Over time, I built my skills up to writing scripts, functions,
-and modules. Don't allow yourself to become overwhelmed by some of the more advanced examples you
-may see on the internet. No one is a natural expert with PowerShell. We were all beginners at
-one time.
+# Chapter 4 - One-Liners and the pipeline
-I have a bit of advice to offer those of you who are still using the GUI for administration:
-install the management tools on your admin workstation and manage your servers remotely. This way it
-won't matter if the server is running a GUI or Server Core installation of the operating system.
-It's going to help prepare you for managing servers remotely with PowerShell.
+When I started learning PowerShell, I initially relied on the Graphical User Interface (GUI) for
+tasks that seemed too complex for simple PowerShell commands. However, as I continued to learn, I
+improved my skills and moved from basic one-liners to creating scripts, functions, and modules. It's
+important to remember that feeling overwhelmed by advanced examples online is normal. No one starts
+as an expert in PowerShell; we all start as beginners.
-As with previous chapters, be sure to follow along on your Windows 10 lab environment computer.
+For those who primarily use the GUI for administrative tasks, install the management tools on your
+administrative workstation to remotely manage your servers. Whether your server uses a GUI or the
+Server Core OS installation, this approach is beneficial. It's a practical way to familiarize
+yourself with remote server management in preparation for performing administrative tasks with
+PowerShell.
+
+As with the previous chapters, try these concepts in your lab environment.
## One-Liners
-A PowerShell one-liner is one continuous pipeline and not necessarily a command that's on one
-physical line. Not all commands that are on one physical line are one-liners.
+A PowerShell one-liner is one continuous pipeline. It's a common misconception that a command on one
+physical line is a PowerShell one-liner, but this isn't always true.
-Even though the following command is on multiple physical lines, it's a PowerShell one-liner because
-it's one continuous pipeline. It could be written on one physical line, but I've chosen to line
-break at the pipe symbol. The pipe symbol is one of the characters where a natural line break is
-allowed in PowerShell.
+For instance, consider the following example: the command extends over multiple physical lines, yet
+it's a PowerShell one-liner because it forms a continuous pipeline. Line-breaking a lengthy
+one-liner at the pipe symbol, a natural breaking point in PowerShell, is recommended to enhance
+readability and clarity. This strategic use of line breaks improves readability without disrupting
+the flow of the pipeline.
```powershell
Get-Service |
- Where-Object CanPauseAndContinue -eq $true |
+ Where-Object CanPauseAndContinue -eq $true |
Select-Object -Property *
```
@@ -43,13 +46,13 @@ CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Workstation
-DependentServices : {SessionEnv, Netlogon, Browser}
+DependentServices : {SessionEnv, Netlogon}
MachineName : .
ServiceName : LanmanWorkstation
ServicesDependedOn : {NSI, MRxSmb20, Bowser}
-ServiceHandle : SafeServiceHandle
+ServiceHandle :
Status : Running
-ServiceType : Win32ShareProcess
+ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Automatic
Site :
Container :
@@ -64,7 +67,7 @@ DependentServices : {}
MachineName : .
ServiceName : Netlogon
ServicesDependedOn : {LanmanWorkstation}
-ServiceHandle : SafeServiceHandle
+ServiceHandle :
Status : Running
ServiceType : Win32ShareProcess
StartType : Automatic
@@ -81,9 +84,9 @@ DependentServices : {}
MachineName : .
ServiceName : vmicheartbeat
ServicesDependedOn : {}
-ServiceHandle : SafeServiceHandle
+ServiceHandle :
Status : Running
-ServiceType : Win32ShareProcess
+ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
@@ -98,9 +101,9 @@ DependentServices : {}
MachineName : .
ServiceName : vmickvpexchange
ServicesDependedOn : {}
-ServiceHandle : SafeServiceHandle
+ServiceHandle :
Status : Running
-ServiceType : Win32ShareProcess
+ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
@@ -115,9 +118,9 @@ DependentServices : {}
MachineName : .
ServiceName : vmicrdv
ServicesDependedOn : {}
-ServiceHandle : SafeServiceHandle
+ServiceHandle :
Status : Running
-ServiceType : Win32ShareProcess
+ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
@@ -132,44 +135,61 @@ DependentServices : {}
MachineName : .
ServiceName : vmicshutdown
ServicesDependedOn : {}
-ServiceHandle : SafeServiceHandle
+ServiceHandle :
Status : Running
-ServiceType : Win32ShareProcess
+ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
-Name : vmictimesync
-RequiredServices : {VmGid}
+Name : vmicvss
+RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
-DisplayName : Hyper-V Time Synchronization Service
+DisplayName : Hyper-V Volume Shadow Copy Requestor
DependentServices : {}
MachineName : .
-ServiceName : vmictimesync
-ServicesDependedOn : {VmGid}
-ServiceHandle : SafeServiceHandle
+ServiceName : vmicvss
+ServicesDependedOn : {}
+ServiceHandle :
Status : Running
-ServiceType : Win32ShareProcess
+ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
-Name : vmicvss
+Name : webthreatdefsvc
+RequiredServices : {RpcSs, wtd}
+CanPauseAndContinue : True
+CanShutdown : True
+CanStop : True
+DisplayName : Web Threat Defense Service
+DependentServices : {}
+MachineName : .
+ServiceName : webthreatdefsvc
+ServicesDependedOn : {RpcSs, wtd}
+ServiceHandle :
+Status : Running
+ServiceType : Win32OwnProcess, Win32ShareProcess
+StartType : Manual
+Site :
+Container :
+
+Name : webthreatdefusersvc_644de
RequiredServices : {}
CanPauseAndContinue : True
-CanShutdown : False
+CanShutdown : True
CanStop : True
-DisplayName : Hyper-V Volume Shadow Copy Requestor
+DisplayName : Web Threat Defense User Service_644de
DependentServices : {}
MachineName : .
-ServiceName : vmicvss
+ServiceName : webthreatdefusersvc_644de
ServicesDependedOn : {}
-ServiceHandle : SafeServiceHandle
+ServiceHandle :
Status : Running
-ServiceType : Win32ShareProcess
-StartType : Manual
+ServiceType : 240
+StartType : Automatic
Site :
Container :
@@ -179,30 +199,38 @@ CanPauseAndContinue : True
CanShutdown : True
CanStop : True
DisplayName : Windows Management Instrumentation
-DependentServices : {wscsvc, NcaSvc, iphlpsvc}
+DependentServices : {}
MachineName : .
ServiceName : Winmgmt
ServicesDependedOn : {RPCSS}
-ServiceHandle : SafeServiceHandle
+ServiceHandle :
Status : Running
-ServiceType : Win32ShareProcess
+ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Automatic
Site :
Container :
```
-Natural line breaks can occur at commonly used characters including comma (`,`) and opening brackets
-(`[`), braces (`{`), and parenthesis (`(`). Others that aren't so common include the semicolon
-(`;`), equals sign (`=`), and both opening single and double quotes (`'`,`"`).
+Natural line breaks can occur at commonly used characters, including comma (`,`) and opening
+brackets (`[`), braces (`{`), and parenthesis (`(`). Others that aren't so common include the
+semicolon (`;`), equals sign (`=`), and both opening single and double quotes (`'`,`"`).
+
+Using the backtick (`` ` ``) or grave accent character as a line continuation is controversial. It's
+best to avoid it if possible. Using a backtick following a natural line break character is a common
+mistake. This redundancy is unnecessary and can clutter the code.
-Using the backtick (`` ` ``) or grave accent character as a line continuation character is a
-controversial topic. My recommendation is to try to avoid it if at all possible. I often see
-PowerShell commands written using a backtick immediately after a natural line break character.
-There's no reason for it to be there.
+The commands in the following example execute correctly from the PowerShell console. However,
+attempting to run them in the console pane of the PowerShell Integrated Scripting Environment (ISE)
+results in an error. This difference occurs because, unlike the PowerShell console, the console pane
+of the ISE doesn't automatically anticipate the continuation of a command onto the next line. To
+prevent this issue, press Shift+Enter in the console pane of the ISE instead
+of Enter when you need to extend a command across multiple lines. This key combination
+signals to the ISE that the command is continuing on the following line, preventing the execution
+that leads to errors.
```powershell
Get-Service -Name w32time |
->> Select-Object -Property *
+ Select-Object -Property *
```
```Output
@@ -216,23 +244,17 @@ DependentServices : {}
MachineName : .
ServiceName : w32time
ServicesDependedOn : {}
-ServiceHandle : SafeServiceHandle
+ServiceHandle :
Status : Running
-ServiceType : Win32ShareProcess
+ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
```
-The commands shown in the previous two examples work fine in the PowerShell console. But if you try
-to run them in the console pane of the PowerShell ISE, they'll generate an error. The console pane
-of the PowerShell ISE doesn't wait for the rest of the command to be entered on the next line like
-the PowerShell console does. To avoid this problem in the console pane of the PowerShell ISE, use
-Shift+Enter instead of just pressing Enter when continuing a
-command on another line.
-
-This next example isn't a PowerShell one-liner because it's not one continuous pipeline. It's two
-separate commands on one line, separated by a semicolon.
+This next example doesn't qualify as a PowerShell one-liner because it's not one continuous
+pipeline. Instead, it's two separate commands placed on a single line, separated by a semicolon.
+This semicolon indicates the end of one command and the beginning of another.
```powershell
$Service = 'w32time'; Get-Service -Name $Service
@@ -244,21 +266,22 @@ Status Name DisplayName
Running w32time Windows Time
```
-Many programming and scripting languages require a semicolon at the end of each line. While they can
-be used that way in PowerShell, it's not recommended because they're not needed.
+Many programming and scripting languages require a semicolon at the end of each line. However, in
+PowerShell, semicolons at the end of lines are unnecessary and not recommended. You should avoid
+them for cleaner and more readable code.
-## Filtering Left
+## Filter Left
-The results of the commands shown in this chapter have been filtered down to a subset. For example,
-`Get-Service` was used with the **Name** parameter to filter the list of services that were returned
-to only the Windows Time service.
+This chapter demonstrates how to filter the results of various commands.
-In the pipeline, you always want to filter the results down to what you're looking for as early as
-possible. This is accomplished using parameters on the first command or, the one to the far left.
-This is sometimes called _filtering left_.
+It's a best practice in PowerShell to filter the results as early as possible in the pipeline.
+Achieving this involves applying filters using parameters on the initial command, usually at the
+beginning of the pipeline. This is commonly referred to as _filtering left_.
-The following example uses the **Name** parameter of `Get-Service` to immediately filter the results
-to the Windows Time service only.
+To illustrate this concept, consider the following example: Use the **Name** parameter of
+`Get-Service` to filter the results at the beginning of the pipeline, returning only the details for
+the Windows Time service. This method demonstrates efficient data retrieval, ensuring you only
+return the necessary and relevant information.
```powershell
Get-Service -Name w32time
@@ -270,8 +293,9 @@ Status Name DisplayName
Running w32time Windows Time
```
-It's not uncommon to see examples where the command is piped to `Where-Object` to perform the
-filtering.
+It's common to see online examples of a PowerShell command being piped to the `Where-Object` cmdlet
+to filter its results. This technique is inefficient if an earlier command in the pipeline has a
+parameter to perform the filtering.
```powershell
Get-Service | Where-Object Name -eq w32time
@@ -283,107 +307,127 @@ Status Name DisplayName
Running W32Time Windows Time
```
-The first example filters at the source and only returns the results for the Windows Time service.
-The second example returns all the services then pipes them to another command to perform the
-filtering. While this may not seem like a big deal in this example, imagine if you were querying a
-list of Active Directory users. Do you really want to return the information for many thousands of
-user accounts from Active Directory only to pipe them to another command that filters them down to a
-tiny subset? My recommendation is to always filter left even when it doesn't seem to matter. You'll
-be so use to it that you'll automatically filter left when it really does matter.
+The first example demonstrates filtering directly at the source, returning results specifically for
+the Windows Time service. In contrast, the second example retrieves all services and then uses
+another command to filter the results. This might seem insignificant in small-scale scenarios, but
+consider a situation involving a large dataset, like Active Directory. It's inefficient to retrieve
+details for thousands of user accounts only to narrow them down to a small subset. Practice
+_filtering left_ — applying filters as early as possible in the command sequence — even in seemingly
+trivial cases. This habit ensures efficiency in more complex scenarios where it becomes more
+important.
+
+### Command sequencing for effective filtering
+
+There's a misconception that the order of commands in PowerShell is inconsequential, but this is a
+misunderstanding. The sequence in which you arrange commands, particularly when filtering, is
+important. For example, suppose you're using `Select-Object` to choose specific properties and
+`Where-Object` to filter. In that case, it's essential to apply the filtering first. Failing to do
+so means the necessary properties might not be available in the pipeline for filtering, leading to
+ineffective or erroneous results.
-I once had someone tell me that the order you specify the commands in doesn't matter. That couldn't
-be further from the truth. The order that the commands are specified in does indeed matter when
-performing filtering. For example, consider the scenario where you are using `Select-Object` to
-select only a few properties and `Where-Object` to filter on properties that won't be in the
-selection. In that scenario, the filtering must occur first, otherwise the property won't exist
-in the pipeline when try to perform the filtering.
+The following example fails to produce results because the **CanPauseAndContinue** property is
+absent when `Select-Object` is piped to `Where-Object`. This is because the **CanPauseAndContinue**
+property wasn't included in the selection made by `Select-Object`. Effectively, it's excluded or
+filtered out.
```powershell
Get-Service |
-Select-Object -Property DisplayName, Running, Status |
-Where-Object CanPauseAndContinue
+ Select-Object -Property DisplayName, Running, Status |
+ Where-Object CanPauseAndContinue
```
-The command in the previous example doesn't return any results because the **CanPauseAndContinue**
-property doesn't exist when the results of `Select-Object` are piped to `Where-Object`. That
-particular property wasn't "selected". In essence, it was filtered out. Reversing the order of
-`Select-Object` and `Where-Object` produces the desired results.
+Reversing the order of `Select-Object` and `Where-Object` produces the desired results.
```powershell
Get-Service |
-Where-Object CanPauseAndContinue |
-Select-Object -Property DisplayName, Status
+ Where-Object CanPauseAndContinue |
+ Select-Object -Property DisplayName, Status
```
```Output
DisplayName Status
----------- ------
-Workstation Running
-Netlogon Running
-Hyper-V Heartbeat Service Running
-Hyper-V Data Exchange Service Running
-Hyper-V Remote Desktop Virtualization Service Running
-Hyper-V Guest Shutdown Service Running
-Hyper-V Time Synchronization Service Running
-Hyper-V Volume Shadow Copy Requestor Running
-Windows Management Instrumentation Running
+Workstation Running
+Netlogon Running
+Hyper-V Heartbeat Service Running
+Hyper-V Data Exchange Service Running
+Hyper-V Remote Desktop Virtualization Service Running
+Hyper-V Guest Shutdown Service Running
+Hyper-V Volume Shadow Copy Requestor Running
+Web Threat Defense Service Running
+Web Threat Defense User Service_644de Running
+Windows Management Instrumentation Running
```
## The Pipeline
-As you've seen in many of the examples shown so far throughout this book, many times the output of
-one command can be used as input for another command. In Chapter 3, `Get-Member` was used to
-determine what type of object a command produces. Chapter 3 also showed using the **ParameterType**
-parameter of `Get-Command` to determine what commands accepted that type of input, although not
-necessarily by pipeline input.
+As seen in many examples throughout this book, you can often use the output of one command as input
+for another command. In Chapter 3, `Get-Member` was used to determine what type of object a command
+produces.
-Depending on how thorough a commands help is, it may include an **INPUTS** and **OUTPUTS** section.
+Chapter 3 also showed using the **ParameterType** parameter of `Get-Command` to determine what
+commands accepted that type of input. Depending on how thorough help for a command is, it might
+include an **INPUTS** and **OUTPUTS** section.
+
+The **INPUTS** section indicates that you can pipe a **ServiceController** or a **String** object to
+the `Stop-Service` cmdlet.
```powershell
help Stop-Service -Full
```
+The following output is abbreviated to show the relevant portion of the help.
+
```Output
...
INPUTS
- System.ServiceProcess.ServiceController, System.String
- You can pipe a service object or a string that contains the name of a service
- to this cmdlet.
+ System.ServiceProcess.ServiceController
+ You can pipe a service object to this cmdlet.
+
+ System.String
+ You can pipe a string that contains the name of a service to this
+ cmdlet.
+
OUTPUTS
- None, System.ServiceProcess.ServiceController
- This cmdlet generates a System.ServiceProcess.ServiceController object that
- represents the service, if you use the PassThru parameter. Otherwise, this
- cmdlet does not generate any output.
+ None
+ By default, this cmdlet returns no output.
+
+ System.ServiceProcess.ServiceController
+ When you use the PassThru parameter, this cmdlet returns a
+ ServiceController object representing the service.
...
```
-Only the relevant section of the help is shown in the previous results. As you can see, the
-**INPUTS** section states that a **ServiceController** or a **String** object can be piped to the
-`Stop-Service` cmdlet. It doesn't tell you which parameters accept that type of input. One of the
-easiest ways to determine that information is to look through the different parameters in the full
-version of the help for the `Stop-Service` cmdlet.
+However, it doesn't specify which parameters accept this type of input. You can determine that
+information by checking the different parameters in the full version of the help for the
+`Stop-Service` cmdlet.
```powershell
help Stop-Service -Full
```
+Once again, only the relevant help is shown in the following results. Notice that the
+**DisplayName** parameter doesn't accept pipeline input. The **InputObject** parameter accepts
+pipeline input by value for **ServiceController** objects. The Name parameter accepts pipeline input
+by value for **String** objects and pipeline input **by property name**.
+
```Output
...
--DisplayName
- Specifies the display names of the services to stop. Wildcard characters are
- permitted.
+-DisplayName
+ Specifies the display names of the services to stop. Wildcard
+ characters are permitted.
Required? true
Position? named
Default value None
Accept pipeline input? False
- Accept wildcard characters? false
+ Accept wildcard characters? true
--InputObject
- Specifies ServiceController objects that represent the services to stop. Enter a
- variable that contains the objects, or type a command or expression that gets the
- objects.
+-InputObject
+ Specifies ServiceController objects that represent the services to
+ stop. Enter a variable that contains the objects, or type a command
+ or expression that gets the objects.
Required? true
Position? 0
@@ -391,33 +435,29 @@ help Stop-Service -Full
Accept pipeline input? True (ByValue)
Accept wildcard characters? false
--Name
- Specifies the service names of the services to stop. Wildcard characters are
- permitted.
+-Name
+ Specifies the service names of the services to stop. Wildcard
+ characters are permitted.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
- Accept wildcard characters? false
+ Accept wildcard characters? true
...
```
-Once again, I've only shown the relevant portion of the help in the previous set of results. Notice
-that the **DisplayName** parameter doesn't accept pipeline input, the **InputObject** parameter
-accepts pipeline input **by value** for **ServiceController** objects, and the **Name** parameter
-accepts pipeline input **by value** for **string** objects. It also accepts pipeline input **by
-property name**.
+When handling pipeline input, a parameter that accepts pipeline input both **by property name** and
+**by value** prioritizes **by value** binding first. If this method fails, it attempts to process
+pipeline input **by property name**. However, the term **by value** can be misleading. A more
+accurate description is **by type**.
-When a parameter accepts pipeline input by both property name and by value, it always tries **by
-value** first. If **by value** fails, then it tries **by property name**. **By value** is a little
-misleading. I prefer to call it **by type**. This means if you pipe the results of a command that
-produces a **ServiceController** object type to `Stop-Service`, it binds that input to the
-**InputObject** parameter. But if you pipe the results of a command that produces **String** output
-to `Stop-Service`, it binds it to the **Name** parameter. If you pipe the results of a command that
-doesn't produce a **ServiceController** or **String** object to `Stop-Service`, but it does produce
-output containing a property called **Name**, then it binds the **Name** property from the output to
-the **Name** parameter of `Stop-Service`.
+For instance, if you pipe the output of a command that generates a **ServiceController** object to
+`Stop-Service`, this output is bound to the **InputObject** parameter. If the piped command produces
+a **String** object, it associates the output with the **Name** parameter. If you pipe output from a
+command that doesn't produce a **ServiceController** or **String** object, but does include a
+property named **Name**, `Stop-Service` binds the value of the **Name** property to its **Name**
+parameter.
Determine what type of output the `Get-Service` command produces.
@@ -425,22 +465,22 @@ Determine what type of output the `Get-Service` command produces.
Get-Service -Name w32time | Get-Member
```
+`Get-Service` produces a **ServiceController** object type.
+
```Output
TypeName: System.ServiceProcess.ServiceController
```
-`Get-Service` produces a ServiceController object type.
-
-As you previously saw in the help, the **InputObject** parameter of `Stop-Service` accepts
-**ServiceController** objects via the pipeline **by value** (by type). This means that when the
-results of the `Get-Service` cmdlet are piped to `Stop-Service`, they bind to the **InputObject**
-parameter of `Stop-Service`.
+As shown in the help for `Stop-Service` cmdlet, the **InputObject** parameter accepts
+**ServiceController** objects through the pipeline **by value**. This implies that when you pipe the
+output of the `Get-Service` cmdlet to `Stop-Service`, the **ServiceController** objects produced by
+`Get-Service` bind to the **InputObject** parameter of `Stop-Service`.
```powershell
Get-Service -Name w32time | Stop-Service
```
-Now to try string input. Pipe `w32time` to `Get-Member` just to confirm that it's a string.
+Now try string input. Pipe `w32time` to `Get-Member` to confirm that it's a string.
```powershell
'w32time' | Get-Member
@@ -450,33 +490,37 @@ Now to try string input. Pipe `w32time` to `Get-Member` just to confirm that it'
TypeName: System.String
```
-As previously shown in the help, piping a string to `Stop-Service` binds it **by value** to the
-**Name** parameter of `Stop-Service`. Test this by piping `w32time` to `Stop-Service`.
+The PowerShell help documentation illustrates that when you pipe a string to `Stop-Service`, it
+binds to the **Name** parameter **by value**. Conduct a practical test to see this in action: pipe
+the string `w32time` to `Stop-Service`. This example demonstrates how `Stop-Service` processes the
+string `w32time` as the name of the service to stop. Execute the following command to observe this
+binding and command execution in action.
+
+Notice that `w32time` is enclosed in single quotes. In PowerShell, it's a best practice to use
+single quotes for static strings, reserving double quotes for situations where the string contains
+variables that require expansion. Single quotes tell PowerShell to treat the content literally
+without parsing for variables. This approach not only ensures accuracy in how your script interprets
+the string but also enhances performance, as PowerShell expends less processing effort on strings
+within single quotes.
```powershell
'w32time' | Stop-Service
```
-Notice that in the previous example, I used single quotes around the string `w32time`. In
-PowerShell, you should always use single quotes instead of double quotes unless the contents of the
-quoted string contains a variable that needs to be expanded to its actual value. By using single
-quotes, PowerShell doesn't have to parse the contents contained within the quotes so your code runs
-a little faster.
-
Create a custom object to test pipeline input by property name for the **Name** parameter of
`Stop-Service`.
```powershell
-$CustomObject = [pscustomobject]@{
- Name = 'w32time'
- }
+$customObject = [pscustomobject]@{
+ Name = 'w32time'
+}
```
The contents of the **CustomObject** variable is a **PSCustomObject** object type and it contains a
property named **Name**.
```powershell
-$CustomObject | Get-Member
+$customObject | Get-Member
```
```Output
@@ -491,96 +535,115 @@ ToString Method string ToString()
Name NoteProperty string Name=w32time
```
-If you were to surround the `$CustomObject` variable with quotes, you want to use double quotes.
-Otherwise, using single quotes, the literal string `$CustomObject` is piped to `Get-Member` instead
-of the value contained by the variable.
+When working with variables in PowerShell, such as `$customObject` in this example, it's important
+to use double quotes if you need to enclose the variable in quotes. Double quotes allow for variable
+expansion — PowerShell evaluates the variable and uses its value. For example, if you enclose
+`$customObject` in double quotes and pipe it to `Get-Member`, PowerShell processes the value of
+`$customObject`. In contrast, using single quotes would result in piping the literal string
+`$customObject` to `Get-Member`, not the value of the variable. This distinction is important for
+scenarios where you need to evaluate the value of variables.
-Although piping the contents of `$CustomObject` to `Stop-Service` cmdlet binds to the **Name**
-parameter, this time it binds **by property name** instead of **by value** because the contents of
-`$CustomObject` is an object that has a property named **Name**.
+When piping the contents of the `$customObject` variable to the `Stop-Service` cmdlet, the binding
+to the **Name** parameter occurs by **property name** rather than **by value**. This is because
+`$customObject` is an object that contains a property named **Name**. In this scenario, PowerShell
+identifies the **Name** property within `$customObject` and uses its value for the **Name**
+parameter of `Stop-Service`.
-In this example, I create another custom object using a different property name, such as
-**Service**.
+Create another custom object using a different property name, such as **Service**.
```powershell
-$CustomObject = [pscustomobject]@{
- Service = 'w32time'
+$customObject = [pscustomobject]@{
+ Service = 'w32time'
}
```
-An error is generated when trying to pipe `$CustomObject` to `Stop-Service` because it doesn't
-produce a **ServiceController** or **String** object, and it doesn't have a property named **Name**.
+An error occurs while trying to stop the `w32time` service by piping `$customObject` to
+`Stop-Service`. The pipeline binding fails because `$customObject` doesn't produce a
+**ServiceController** or **String** object and doesn't contain a **Name** property.
```powershell
-$CustomObject | Stop-Service
+$customObject | Stop-Service
```
```Output
-Stop-Service : Cannot find any service with service name '@{Service=w32time}'.
+Stop-Service : Cannot find any service with service name
+'@{Service=w32time}'.
At line:1 char:17
-+ $CustomObject | Stop-Service
-+
- + CategoryInfo : ObjectNotFound: (@{Service=w32time}:String) [Stop-Service]
- , ServiceCommandException
- + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.S
- topServiceCommand
++ $customObject | Stop-Service
++ ~~~~~~~~~~~~
+ + CategoryInfo : ObjectNotFound: (@{Service=w32time}:String) [
+ Stop-Service], ServiceCommandException
+ + FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShe
+ ll.Commands.StopServiceCommand
```
-If the output of one command doesn't line up with the pipeline input options for another command,
-`Select-Object` can be used to rename the property so that the properties lineup correctly.
+When the output property names of one command don't match the pipeline input requirements of another
+command, you can use `Select-Object` to rename the property names so they line up correctly.
+
+In the following example, use `Select-Object` to rename the **Service** property to a property named
+**Name**.
+
+At first glance, the syntax of this example might appear complex. However, it's essential to
+understand that more than copying and pasting code is required to learn the syntax. Instead, take
+the time to type out the code manually. This hands-on practice helps you remember the syntax, and it
+becomes more intuitive with repeated effort. Utilizing multiple monitors or split screen can also
+aid in the learning process. Display the example code on one screen while actively typing and
+experimenting with it on another. This setup makes it easier to follow along and enhances your
+understanding and retention of the syntax.
```powershell
-$CustomObject |
- Select-Object -Property @{name='Name';expression={$_.Service}} |
+$customObject |
+ Select-Object -Property @{name='Name';expression={$_.Service}} |
Stop-Service
```
-In this example, `Select-Object` was used to rename the **Service** property to a property named
-**Name**.
-
-The syntax this example may seem a little complicated at first. What I have learned is that you'll
-never learn the syntax by copy and pasting code. Take the time to type the code in. After a few
-times, it becomes second nature. Having multiple monitors is a huge benefit because you can display
-the example code on one screen and type it in on another one.
-
-Occasionally, you may want to use a parameter that doesn't accept pipeline input. The following
-example demonstrates using the output of one command as input for another. First save the display
-name for a couple of Windows services into a text file.
+There are instances where you might need to use a parameter that doesn't accept pipeline input. In
+such cases, you can still use the output of one command as the input for another. First, capture and
+save the display names of a few specific Windows services into a text file. This step allows you to
+use the saved data as input for another command.
```powershell
'Background Intelligent Transfer Service', 'Windows Time' |
-Out-File -FilePath $env:TEMP\services.txt
+ Out-File -FilePath $env:TEMP\services.txt
```
-You can run the command that provides the needed output within parentheses as the value for the
-parameter of the command requiring the input.
+You can use parentheses to pass the output of one command as input for a parameter to another
+command.
```powershell
Stop-Service -DisplayName (Get-Content -Path $env:TEMP\services.txt)
```
-This is just like order of operations in Algebra for those of you who remember how it works. The
-command within parentheses always runs prior to the outer portion of the command.
+This concept is like the order of operations in Algebra. Just as mathematical operations within
+parentheses are computed first, the command enclosed in parentheses is executed before the outer
+command.
## PowerShellGet
-PowerShellGet is a PowerShell module that contains commands for discovering, installing, publishing,
-and updating PowerShell modules (and other artifacts) to or from a NuGet repository. PowerShellGet
-ships with PowerShell version 5.0 and higher. It is available as a separate download for PowerShell
-version 3.0 and higher.
-
-Microsoft hosts an online NuGet repository called the [PowerShell Gallery][PowerShell Gallery].
-Although this repository is hosted by Microsoft, the majority of the modules contained within the
-repository aren't written by Microsoft. Any code obtain from the PowerShell Gallery should be
-thoroughly reviewed in an isolated test environment before being considered suitable for use in a
-production environment.
-
-Most companies will want to host their own internal private NuGet repository where they can post
-their internal use only modules as well as modules that they've downloaded from other sources once
-they've validated them as being non-malicious.
-
-Use the `Find-Module` cmdlet that's part of the PowerShellGet module to find a module in the
-PowerShell Gallery that I wrote named MrToolkit.
+**PowerShellGet**, a module included with PowerShell version 5.0 and higher, provides commands to
+discover, install, update, and publish PowerShell modules and other items in a NuGet repository. For
+those using PowerShell version 3.0 and above, PowerShellGet is also available as a separate
+download.
+
+The [PowerShell Gallery][powerShell-gallery] is an online repository hosted by Microsoft, designed
+as a central hub for sharing PowerShell modules, scripts, and other resources. While Microsoft hosts
+the PowerShell Gallery, the PowerShell community contributes most of the available modules and
+scripts. Given the source of these modules and scripts, exercise caution before integrating any code
+from the PowerShell Gallery into your environment. Review and test downloads from the PowerShell
+Gallery in an isolated test environment. This process ensures the code is secure and reliable, works
+as expected, and safeguards your environment from potential issues or vulnerabilities arising from
+unvetted code.
+
+Many organizations opt to establish their own internal, private NuGet repository. This repository
+serves a dual purpose. First, it acts as a secure location for storing modules developed in-house,
+intended solely for internal use. Secondly, it provides a vetted collection of modules sourced
+externally, including those from public repositories. Companies typically undertake a thorough
+validation process before adding these external modules to the internal repository. This process is
+important to ensure the modules are free from malicious content and align with the security and
+operational standards of the company.
+
+Use the `Find-Module` cmdlet that's part of the **PowerShellGet** module to find a module in the
+PowerShell Gallery that I wrote named **MrToolkit**.
```powershell
Find-Module -Name MrToolkit
@@ -588,36 +651,37 @@ Find-Module -Name MrToolkit
```Output
NuGet provider is required to continue
-PowerShellGet requires NuGet provider version '2.8.5.201' or newer to interact with
-NuGet-based repositories. The NuGet provider must be available in 'C:\Program
-Files\PackageManagement\ProviderAssemblies' or
-'C:\Users\MrAdmin\AppData\Local\PackageManagement\ProviderAssemblies'. You can also
-install the NuGet provider by running 'Install-PackageProvider -Name NuGet
--MinimumVersion 2.8.5.201 -Force'. Do you want PowerShellGet to install and import the
-NuGet provider now?
+PowerShellGet requires NuGet provider version '2.8.5.201' or newer to
+interact with NuGet-based repositories. The NuGet provider must be available
+ in 'C:\Program Files\PackageManagement\ProviderAssemblies' or
+'C:\Users\mikefrobbins\AppData\Local\PackageManagement\ProviderAssemblies'.
+You can also install the NuGet provider by running 'Install-PackageProvider
+-Name NuGet -MinimumVersion 2.8.5.201 -Force'. Do you want PowerShellGet to
+install and import the NuGet provider now?
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):
-Version Name Repository Description
-------- ---- ---------- -----------
-1.1 MrToolkit PSGallery Misc PowerShell Tools
+Version Name Repository Description
+------- ---- ---------- -----------
+1.3 MrToolkit PSGallery Misc PowerShell Tools
```
-The first time you use one of the commands from the PowerShellGet module, you'll be prompted to
+The first time you use one of the commands from the **PowerShellGet** module, you're prompted to
install the NuGet provider.
-To install the MrToolkit module, pipe the previous command to `Install-Module`.
+To install the **MrToolkit** module, pipe the previous command to `Install-Module`.
```powershell
-Find-Module -Name MrToolkit | Install-Module
+Find-Module -Name MrToolkit | Install-Module -Scope CurrentUser
```
```Output
Untrusted repository
-You are installing the modules from an untrusted repository. If you trust this
-repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet.
-Are you sure you want to install the modules from
-'https://www.powershellgallery.com/api/v2/'?
-[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): y
+You are installing the modules from an untrusted repository. If you trust
+this repository, change its InstallationPolicy value by running the
+Set-PSRepository cmdlet. Are you sure you want to install the modules from
+'https://www.powershellgallery.com/api/v2'?
+[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help
+(default is "N"):y
```
Since the PowerShell Gallery is an untrusted repository, it prompts you to approve the installation
@@ -625,48 +689,66 @@ of the module.
## Finding pipeline input the easy way
-The MrToolkit module contains a function named `Get-MrPipelineInput`. This cmdlet can be used to
-easily determine which parameters of a command accept pipeline input, what type of object they
-accept, and if they accept pipeline input **by value** or **by property name**.
+The **MrToolkit** module includes a function named `Get-MrPipelineInput`. This cmdlet is designed to
+provide users with a convenient method for identifying the parameters of a command capable of
+accepting pipeline input. Specifically, it reveals three key aspects:
+
+- Which parameters of a command can receive pipeline input
+- The type of object each parameter accepts
+- Whether they accept pipeline input **by value** or **by property name**
+
+This capability dramatically simplifies the process of understanding and utilizing the pipeline
+capabilities of PowerShell commands.
+
+The information previously obtained by analyzing the help documentation can be determined using this
+function.
```powershell
-Get-MrPipelineInput -Name Stop-Service
+Get-MrPipelineInput -Name Stop-Service | Format-List
```
```Output
-ParameterName ParameterType ValueFromPipeline ValueFromPipelineByPropertyName
-------------- ------------- ----------------- ---------------
-InputObject System.ServiceProcess.ServiceController[] True False
-Name System.String[] True True
-```
+ParameterName : InputObject
+ParameterType : System.ServiceProcess.ServiceController[]
+ValueFromPipeline : True
+ValueFromPipelineByPropertyName : False
-As you can see, the same information we previously determined by sifting through the help can easily
-be determined with this function.
+ParameterName : Name
+ParameterType : System.String[]
+ValueFromPipeline : True
+ValueFromPipelineByPropertyName : True
+```
## Summary
-In this chapter, you've learned about PowerShell one-liners. You've learned that the number of
-physical lines that a command is on has nothing to do with whether or not it's a PowerShell
-one-liner. You've also learned about filtering left, the pipeline, and PowerShellGet.
+In this chapter, you learned about the intricacies of PowerShell one-liners. You also learned that
+the physical line count of a command is irrelevant to its classification as a PowerShell one-liner.
+Additionally, you learned about key concepts such as filtering left, the pipeline, and
+**PowerShellGet**.
## Review
-1. What is a PowerShell one-liner?
-1. What are some of the characters where natural line breaks can occur in PowerShell?
+1. What's a PowerShell one-liner?
+1. What are some characters where natural line breaks can occur in PowerShell?
1. Why should you filter left?
1. What are the two ways that a PowerShell command can accept pipeline input?
1. Why shouldn't you trust commands found in the PowerShell Gallery?
-## Recommended Reading
+## References
-- [about_Pipelines][about_Pipelines]
-- [about_Command_Syntax][about_Command_Syntax]
-- [about_Parameters][about_Parameters]
+- [about_Pipelines][about-pipelines]
+- [about_Command_Syntax][about-command-syntax]
+- [about_Parameters][about-parameters]
- [PowerShellGet: The BIG EASY way to discover, install, and update PowerShell modules][psget]
+## Next steps
+
+In the next chapter, you'll learn about formatting, aliases, providers, and comparison operators.
+
-[about_Pipelines]: /powershell/module/microsoft.powershell.core/about/about_pipelines
-[about_Command_Syntax]: /powershell/module/microsoft.powershell.core/about/about_command_syntax
-[about_Parameters]: /powershell/module/microsoft.powershell.core/about/about_parameters
+
+[about-pipelines]: /powershell/module/microsoft.powershell.core/about/about_pipelines
+[about-command-syntax]: /powershell/module/microsoft.powershell.core/about/about_command_syntax
+[about-parameters]: /powershell/module/microsoft.powershell.core/about/about_parameters
[psget]: https://mikefrobbins.com/2015/04/23/powershellget-the-big-easy-way-to-discover-install-and-update-powershell-modules/
-[PowerShell Gallery]: https://www.powershellgallery.com/
+[powerShell-gallery]: https://www.powershellgallery.com/
diff --git a/reference/includes/language-spec.md b/reference/includes/language-spec.md
new file mode 100644
index 000000000000..45e65f8fab03
--- /dev/null
+++ b/reference/includes/language-spec.md
@@ -0,0 +1,23 @@
+---
+author: sdwheeler
+ms.author: sewhee
+ms.date: 01/08/2025
+ms.topic: include
+---
+
+## Editorial note
+
+> [!IMPORTANT]
+> The _Windows PowerShell Language Specification 3.0_ was published in December 2012 and is based on
+> Windows PowerShell 3.0. This specification does not reflect the current state of PowerShell. There
+> is no plan to update this documentation to reflect the current state. This documentation is
+> presented here for historical reference.
+>
+> The specification document is available as a Microsoft Word document from the Microsoft Download
+> Center at: [https://www.microsoft.com/download/details.aspx?id=36389][01] That Word document has
+> been converted for presentation here on Microsoft Learn. During conversion, some editorial changes
+> have been made to accommodate formatting for the Docs platform. Some typos and minor errors have
+> been corrected.
+
+
+[01]: https://www.microsoft.com/download/details.aspx?id=36389