PowerShell 技能连载 - 查看 PowerShell 当前的文件系统路径

To find out the path your PowerShell is currently using, simply run Get-Location:
要查看 PowerShell 的当前路径,只要用 Get-Location 命令即可:

1
2
3
4
5
PS> Get-Location

Path
----
C:\Users\tobwe

然而,当前路径不一定指向一个文件系统位置。如果您将位置指向注册表,例如这样:

1
2
3
4
5
6
7
PS> cd hkcu:\

PS> Get-Location

Path
----
HKCU:\

如果您想知道 PowerShell 当前使用的文件系统路径,而不管当前使用什么 provider,请使用以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PS> $ExecutionContext.SessionState.Path

CurrentLocation CurrentFileSystemLocation
--------------- -------------------------
HKCU:\ C:\Users\tobwe



PS> $ExecutionContext.SessionState.Path.CurrentFileSystemLocation

Path
----
C:\Users\tobwe


PS> Get-Location

Path
----
HKCU:\

CurrentFileSystemLocation 总是返回文件系统的当前位置,这可能和 Get-Location 返回的不一样。

PowerShell 技能连载 - 探讨 Windows PowerShell 和 PowerShell Core

PowerShell 当前有两个版本:随 Windows 发布、基于完整 .NET 框架的的 “Windows PowerShell”,以及基于 .NET Core、支持跨平台、能够运行在 Nano Server 等平台的 “PowerShell Core”。

面向某个具体 PowerShell 版本的脚本作者可以使用 #requires 语句来确保他们的脚本运行于指定的版本。

例如,要确保一个脚本运行于 PowerShell Core 中,请将这段代码放在脚本顶部:

1
2
#requires -PSEdition Core
Get-Process

请确保把这段代码保存到磁盘。#requires 只对脚本有效。

当您在 Windows 机器上的 “Windows PowerShell” 中运行这段脚本,将会报错:

1
2
3
4
5
6
7
8
9
PS> C:\Users\abc\requires  core.ps1
The script 'requires core.ps1' cannot be run because it contained a "#requires" statement for PowerShell
editions 'Core'. The edition of PowerShell that is required by the script does not match the currently
running PowerShell Desktop edition.
+ CategoryInfo : NotSpecified: (requires core.ps1:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ScriptRequiresUnmatchedPSEdition


PS>

类似地,甚至更重要的是,当您将 “Core” 替换为 “Desktop”,脚本将无法在受限的 “PowerShell Core” 版本中运行。如果您的脚本依赖于传统 Windows 系统中的某些特性,并且依赖于完整 .NET Framework,这种做法十分明智。

PowerShell 技能连载 - Windows PowerShell 和 PowerShell Core

最近关于 PowerShell 版本有一些混淆。在 GitHub 上有一个名为 “PowerShell 6” 的开源倡议

这是否意味着开源的 PowerShell 6 是 PowerShell 5 的继任者,并且最终和 Windows 一起发布?

并不是的。现在只有两个不同的 PowerShell,所谓的 “PowerShell Editions”。

“Windows PowerShell” 就我们所知会持续存在,并且将会随着将来的 Windows 版本发布,对应完整版 .NET Framework。

开源的 PowerShell 6 本意是基于 “PowerShell Core” 工作,这是一个有限的 .NET 子集 (.NET Core)。它的目的是在一个最小化的环境,例如 Nano Server 中运行,并且能够支持 Linux 和 Apple 等不同的平台。

从 PowerShell 5.1 开始,您可以这样检查 “PowerShell Edition”:

1
2
PS> $PSVersionTable.PSEdition
Desktop

“Desktop” 表示您在完整的 .NET Framework 上运行 “Windows PowerShell”。”Core” 表示您在 .NET Core 上运行 “PowerShell Core”。

PowerShell 技能连载 - 设置 Powershell 标题文本

您也许知道可以通过类似这样一行代码改变 PowerShell 宿主窗口的标题文本:

1
PS> $host.UI.RawUI.WindowTitle = "Hello  World!"

如果把这段代码加入 prompt 函数,标题文本就可以每次变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function prompt
{
# get current path
$path = Get-Location

# get current time
$date = Get-Date -Format 'dddd, MMMM dd'

# create title text
$host.UI.RawUI.WindowTitle = ">>$path<< [$date]"

# output prompt
'PS> '
}

PowerShell 每次完成一条命令之后,都会执行 “prompt“ 函数。在标题栏中,您将始终能看到当前的路径和日期,而 PowerShell 编辑器中的命令提示符被简化成 “PS> “。

PowerShell 技能连载 - 清除所有用户变量

在前一个技能中我们演示了如何用类似这样的方法来查找内置的 PowerShell 变量:

1
2
3
4
5
$ps = [PowerShell]::Create()
$null = $ps.AddScript('$null=$host;Get-Variable')
$ps.Invoke()
$ps.Runspace.Close()
$ps.Dispose()

现在我们来做相反的事情,创建一个函数来查找仅由你创建的用户变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Get-UserVariable ($Name = '*')
{
# these variables may exist in certain environments (like ISE, or after use of foreach)
$special = 'ps','psise','psunsupportedconsoleapplications', 'foreach', 'profile'

$ps = [PowerShell]::Create()
$null = $ps.AddScript('$null=$host;Get-Variable')
$reserved = $ps.Invoke() |
Select-Object -ExpandProperty Name
$ps.Runspace.Close()
$ps.Dispose()
Get-Variable -Scope Global |
Where-Object Name -like $Name |
Where-Object { $reserved -notcontains $_.Name } |
Where-Object { $special -notcontains $_.Name } |
Where-Object Name
}

现在可以很容易查找所有由您(或您的脚本)创建并仍然停留在内存中的变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> Get-UserVariable

Name Value
---- -----
hash {Extensions, Link, Options, GPOLink...}
prop lParam
reserved {$, ?, ^, args...}
result {System.Management.Automation.PSVariable, System.Management.Automation.Ques...
varCount 43



PS> Get-UserVariable -Name pr*

Name Value
---- -----
prop lParam

如果要清理您的运行空间,您可以用一行代码清除所有变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PS> Get-UserVariable

Name Value
---- -----
hash {Extensions, Link, Options, GPOLink...}
key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\H...
prop lParam
reserved {$, ?, ^, args...}
result {System.Management.Automation.PSVariable, System.Management.Automation.Ques...
varCount 43



PS> Get-UserVariable | Remove-Variable

PS> Get-UserVariable

PS>

PowerShell 技能连载 - 查找 PowerShell 缺省变量(第三部分)

在前一个技能中我们演示了如何用类似如下的方法来查找内置的 PowerShell 变量:

1
2
3
4
5
$ps = [PowerShell]::Create()
$null = $ps.AddScript('$null=$host;Get-Variable')
$ps.Invoke()
$ps.Runspace.Close()
$ps.Dispose()

显然,这段代码还是漏了一些不是由 PowerShell 核心引擎创建的变量,而是由具体宿主加入的变量,例如 powershell.exe,或者 ISE。这些缺失的变量需要手工添加。幸好不是很多:

1
2
3
4
5
6
7
8
9
10
$ps = [PowerShell]::Create()
$null = $ps.AddScript('$null=$host;Get-Variable')
[System.Collections.ArrayList]$result = $ps.Invoke() |
Select-Object -ExpandProperty Name
$ps.Runspace.Close()
$ps.Dispose()

# add host-specific variables
$special = 'ps','psise','psunsupportedconsoleapplications', 'foreach', 'profile'
$result.AddRange($special)

现在这段代码能够获取包含所有保留 PowerShell 变量的列表,并且如果我们还缺少了某些变量,只需要将它们添加到 $special 列表即可。

顺便说一下,这段代码完美地演示了如何用 [System.Collections.ArrayList] 来创建一个更好的数组。跟常规的 [Object[]] 数组相比,ArrayList 对象拥有例如 AddRange(),能快速批量加入多个元素,等其它方法。

PowerShell 技能连载 - 查找 PowerShell 缺省变量(第二部分)

在前一个技能里我们解释了如何使用独立全新的 PowerShell 实例来获取所有缺省变量。当您仔细查看这些变量,会发现还是丢失了某些变量。

以下是一个稍微修改过的版本,名为 Get-BuiltInPSVariable,能返回所有保留的 PowerShell 变量:

1
2
3
4
5
6
7
8
9
10
11
12
function Get-BuiltInPSVariable($Name='*')
{
# create a new PowerShell
$ps = [PowerShell]::Create()
# get all variables inside of it
$null = $ps.AddScript('$null=$host;Get-Variable')
$ps.Invoke() |
Where-Object Name -like $Name
# dispose new PowerShell
$ps.Runspace.Close()
$ps.Dispose()
}

为了不遗漏任何一个内置的 PowerShell 变量,这个做法使用了 AddScript() 方法来代替 AddCommand(),来执行多于一条命令。有一些 PowerShell 变量要等待至少一条命令执行之后才创建。

您现在可以获取所有的 PowerShell 内置变量,或搜索指定的变量:

1
2
3
4
5
6
7
8
9
10
11
12
PS> Get-BuiltInPSVariable -Name *pref*

Name Value
---- -----
ConfirmPreference High
DebugPreference SilentlyContinue
ErrorActionPreference Continue
InformationPreference SilentlyContinue
ProgressPreference Continue
VerbosePreference SilentlyContinue
WarningPreference Continue
WhatIfPreference False

PowerShell 技能连载 - 查找 PowerShell 缺省变量(第一部分)

有些时候识别出 PowerShell 管理的缺省变量十分有用,这样能帮您区分内置的变量和自定义的变量。Get-Variable 总是输出所有的变量。

以下是一个简单的技巧,使用一个独立、全新的 PowerShell 运行空间来确定内置的 PowerShell 变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
# create a new PowerShell
$ps = [PowerShell]::Create()
# get all variables inside of it
$null = $ps.AddCommand('Get-Variable')
$result = $ps.Invoke()
# dispose new PowerShell
$ps.Runspace.Close()
$ps.Dispose()

# check results
$varCount = $result.Count
Write-Warning "Found $varCount variables."
$result | Out-GridView

当您运行这段代码时,该代码输出找到的变量数量,以及这些变量。

PowerShell 技能连载 - 查找 PowerShell 类

从 PowerShell 5 开始,您可以定义 PowerShell 类。它们是动态定义的,并且存在于内存中。那么要如何知道这些类的名字?

我们首先定义一个简单的,没有任何内容的类:

1
2
3
4
class TestClass
{

}

如何确认内存中确实存在一个名为 “TestClass” 的类?以下是一个名为 Get-PSClass 的工具函数:

1
2
3
4
5
6
7
8
9
10
11
function Get-PSClass($Name = '*')
{
[AppDomain]::CurrentDomain.GetAssemblies() |
Where-Object { $_.GetCustomAttributes($false) |
Where-Object { $_ -is [System.Management.Automation.DynamicClassImplementationAssemblyAttribute]} } |
ForEach-Object { $_.GetTypes() |
Where-Object IsPublic |
Where-Object { $_.Name -like $Name } |
Select-Object -ExpandProperty Name
}
}

执行这个函数后,它会返回当前内存中所有定义的 PowerShell 类(在我们的 PowerShell 例子中,在前几个技能实验中有好几个 PowerShell 类):

1
2
3
4
5
6
PS> Get-PSClass
HelperStuff
Employee
TestClass

PS>

您也可以显示地测试一个类名:

1
2
3
4
5
6
7
8
PS> Get-PSClass -Name TestClass
TestClass

PS> (Get-PSClass -Name TestClass) -ne $null
True

PS> (Get-PSClass -Name TestClassNotExisting) -ne $null
False

您也可以使用通配符。一下代码将返回所有以 “A” 至 “H” 字母开头的类:

1
2
3
PS> Get-PSClass -Name '[A-H]*'
HelperStuff
Employee

PowerShell 技能连载 - 使用 PowerShell 类(二)

从 PowerShell 5 开始,您可以定义 PowerShell 类。您可以使用类来创建新对象,并通过创建一个或多个构造函数,您可以方便地初始化新创建的对象。

让我们看看效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Employee
{
[int]$Id
[string]$Name

Employee([int]$Id, [string]$Name)
{
$this.Id = $Id
$this.Name = $Name
}
Employee ([string]$Name)
{
$this.Id = -1
$this.Name = $Name
}
Employee ()
{
$this.Id = -1
$this.Name = 'Undefined'
}
}

这段代码运行后,将创建一个包含三个构造函数的 “Employee” 新类。以下是如何使用新类的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
PS> [Employee]::new()

Id Name
-- ----
-1 Undefined



PS> [Employee]::new('Tobias')

Id Name
-- ----
-1 Tobias



PS> [Employee]::new(999, 'Tobias')

Id Name
-- ----
999 Tobias



PS>

每次调用都使用一个新的构造函数,并且该类根据需要创建相应的对象。