PowerShell 技能连载 - 探索 WMI

如果您知道 WMI 类查询的名字,Get-WmiObjectGet-CimInstance 两个命令都可以提供丰富的信息。

以下是一个名为 Explore-WMI 的快速的 PowerShell 函数,它可以帮您查找有用的 WMI 类名:

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
26
function Explore-WMI
{
# find all WMI classes that start with "Win32_"...
$class = Get-WmiObject -Class Win32_* -List |
# exclude performance counter classes...
Where-Object { $_.Name -notlike 'Win32_Perf*' } |
# exclude classes with less than 6 properties...
Where-Object { $_.Properties.Count -gt 5 } |
# let the user select one of the found classes
Out-GridView -Title 'Select one' -OutputMode Single

# display selected class name
Write-Warning "Klassenname: $($class.Name)"

# query class...
Get-WmiObject -Class $class.Name |
# and show all of its properties
Select-Object -Property *


# output code
$name = $class.name
" Get-WmiObject -Class $name | Select-Object -property *" | clip.exe

Write-Warning 'Code copied to clipboard. Paste code to try'
}

当运行完这段代码后,调用 Explore-WMI 命令,它将会打开一个 grid view 窗口,显示所有以 “Win32_” 开头的 WMI 类,并且不包括性能计数器类,并且暴露至少 6 个属性。您接下来可以选择其中一个。PowerShell 将会显示这个类的实例和它的所有数据,然后将生成这些结果的命令复制到剪贴板。

通过这种方式可以方便有趣地在 WMI 中搜索有用的信息,并且获取得到这些信息的代码。

PowerShell 技能连载 - 注册缺省的 PowerShell 源

如果您使用 PowerShellGet 模块(默认随着 Windows 10 和 Server 2016 分发),您可以方便地下载和安装共享的 PowerShell 脚本和模块:

1
2
3
4
5
6
7
8
PS> Find-Module -Tag Security

Version Name Repository Description
------- ---- ---------- -----------
2.5.0 Carbon PSGallery Carbon is a PowerShell module for automating t...
0.8.1 ACMESharp PSGallery Client library for the ACME protocol, which is...
2.22 DSInternals PSGallery The DSInternals PowerShell Module exposes seve...
1.2.0.0 DSCEA PSGallery DSCEA is a scanning engine for processing Test...

不过有些时候,机器上缺失了缺省的 PSGallery 源,要还原缺省设置,请使用以下代码:

1
PS> Register-PSRepository -Default

PowerShell 技能连载 - 查找安装的软件

大多数已安装的文件将自己注册在 Windows 注册表中的四个位置。以下是一个名为 Get-InstalledSoftware 的快速 PowerShell 函数,它能够查询所有这些键名,然后输出找到的软件的信息。

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
26
27
28
29
30
31
32
33
34
35
36
function Get-InstalledSoftware
{
param
(
$DisplayName='*',

$DisplayVersion='*',

$UninstallString='*',

$InstallDate='*'

)

# registry locations where installed software is logged
$pathAllUser = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
$pathCurrentUser = "Registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
$pathAllUser32 = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
$pathCurrentUser32 = "Registry::HKEY_CURRENT_USER\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"


# get all values
Get-ItemProperty -Path $pathAllUser, $pathCurrentUser, $pathAllUser32, $pathCurrentUser32 |
# choose the values to use
Select-Object -Property DisplayVersion, DisplayName, UninstallString, InstallDate |
# skip all values w/o displayname
Where-Object DisplayName -ne $null |
# apply user filters submitted via parameter:
Where-Object DisplayName -like $DisplayName |
Where-Object DisplayVersion -like $DisplayVersion |
Where-Object UninstallString -like $UninstallString |
Where-Object InstallDate -like $InstallDate |

# sort by displayname
Sort-Object -Property DisplayName
}

这个函数也演示了如何不使用 PowerShell 驱动器而直接使用原生的注册表路径。方法是在路径前面添加 provider 的名称。在这个例子中是 Registry::

这个函数将所有输出的列(属性)也暴露为参数,所以可以方便地过滤查询结果。以下例子演示了如何查找所有名字包含“Microsoft”的软件:

1
2
3
4
5
6
7
8
9
10
11
PS C:\> Get-InstalledSoftware -DisplayName *Microsoft*

DisplayVersion DisplayName
-------------- -----------
Definition Update for Microsoft Office 2013 (KB3115404) 32-Bit...
15.0.4569.1506 Microsoft Access MUI (English) 2013
15.0.4569.1506 Microsoft Access Setup Metadata MUI (English) 2013
15.0.4569.1506 Microsoft DCF MUI (English) 2013
15.0.4569.1506 Microsoft Excel MUI (English) 2013
15.0.4569.1506 Microsoft Groove MUI (English) 2013
(...)

PowerShell 技能连载 - 禁止 Windows 10 中的 OneDrive

您是否也为任务栏中的 OneDrive 图标感到烦恼?如果您从未使用 OneDrive,以下是两个易于使用的 PowerShell 函数,能够帮助您在资源管理器中隐藏(和显示)OneDrive 图标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Disable-OneDrive
{
$regkey1 = 'Registry::HKEY_CLASSES_ROOT\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}'
$regkey2 = 'Registry::HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}'
Set-ItemProperty -Path $regkey1, $regkey2 -Name System.IsPinnedToNameSpaceTree -Value 0
}


function Enable-OneDrive
{
$regkey1 = 'Registry::HKEY_CLASSES_ROOT\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}'
$regkey2 = 'Registry::HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{018D5C66-4533-4307-9B53-224DE2ED1FE6}'
Set-ItemProperty -Path $regkey1, $regkey2 -Name System.IsPinnedToNameSpaceTree -Value 1
}

执行后不需要重启,结果立即生效。

PowerShell 技能连载 - Get-Service 的替代

Get-Service cmdlet 有一系列缺点。例如,没有一个过滤运行中或停止的服务的参数,并且结果不包含服务的启动模式。

WMI 可以传出这些信息。以下是一个获取最常用服务信息的简单函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Get-ServiceWithWMI
{
param
(
$Name = '*',
$State = '*',
$StartMode = '*'
)

Get-WmiObject -Class Win32_Service |
Where-Object Name -like $Name |
Where-Object State -like $State |
Where-Object StartMode -like $StartMode |
Select-Object -Property Name, DisplayName, StartMode, State, ProcessId, Description

}

以下是调用该命令的方法,这行代码显示所有禁用的服务,包括它们的友好名称和描述:

1
PS C:\> Get-ServiceWithWMI -StartMode Disabled | Out-GridView

PowerShell 技能连载 - 轻量级 Robocopy

Robocopy.exe 是一个非常有用并且功能多样的内置命令,它可以高效地将文件从一个地方复制到另一个地方。不幸的是,该命令有很多选项和开关,使得它很难掌握。

如果您只是希望将文件从 A 处拷贝到 B 处,以下是将 robocopy 封装并将这个怪兽转化为易用的复制命令的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Copy-FileWithRobocopy
{
param
(
[Parameter(Mandatory)]
$Source,

[Parameter(Mandatory)]
$Destination,

$Filter = '*'
)

robocopy.exe $Source $Destination $Filter /S /R:0
}

以下是如何使用新命令的方法:下面这行代码将所有 .log 文件从 Windows 文件夹复制到 c:\logs 文件夹:

1
PS> Copy-FileWithRobocopy -Source $env:windir -Destination c:\logs -Filter *.log

PowerShell 技能连载 - 创建一个清单式的摘要对象

从 PowerShell 3 开始,PSCustomObject 可以将从其他地方收集的有用信息方便地合并进来。以下例子从不同的 WMI 类获取各种信息,并且输出为一个清单。该清单可以传递给其它命令,也可以直接使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# get information from this computer
$Computername = "."

# get basic information (i.e. from WMI)
$comp = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computername
$bios = Get-WmiObject -Class Win32_bios -ComputerName $Computername
$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computername


# combine everything important in one object
[PSCustomObject]@{
ComputerName = $Computername
Timestamp = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
Model = $comp.Model
Manufacturer = $comp.Manufacturer
BIOSVersion = $bios.SMbiosbiosversion
BIOSSerialNumber = $bios.serialnumber
OSVersion = $os.Version
InstallDate = $os.ConvertToDateTime( $os.InstallDate)
LastBoot = $os.ConvertToDateTime($os.lastbootuptime)
LoggedOnUser = $Comp.UserName
}

PowerShell 技能连载 - 从 WikiQuote 搜集引用

当从 CSV 加载数据到 Excel 中时,我们无法指定格式。

1
2
3
4
5
PS> Get-Quote

Text
----
If you don't know anything about computers, just remember that they are machines that do exactly w...
1
2
3
4
5
PS> Get-Quote -Topics men

Text Author
---- ------
But man is not made for defeat. A man can be destroyed but not defeated. Ernest Hemingway (18991...
1
2
3
4
5
6
7
8
PS> Get-Quote -Topics jewelry
WARNING: Topic 'jewelry' not found. Try a different one!

PS> Get-Quote -Topics jewel

Text
----
Cynicism isn't smarter, it's only safer. There's nothing fluffy about optimism . … People have th...

以下脚本首先加载 HTML 内容,然后使用正则表达式来搜集 HTML 中的引用。当然这只适用于原文有规律的情况。wikiquotes 的引用模式是这样的:

1
<li><ul>Quote<ul><li>Author</li></ul></li>

所以以下代码将搜索这个模式,然后清理结构中找到的文本:需要移除 HTML 标签,例如链接,多个空格需要合并为一个空格(通过嵌套函数 Remove-Tag)。

以下是代码:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
function Get-Quote ($Topics='Computer', $Count=1)
{
function Remove-Tag ($Text)
{
$tagCount = 0
$text = -join $Text.ToCharArray().Foreach{
switch($_)
{
'<' { $tagCount++}
'>' { $tagCount--; ' '}
default { if ($tagCount -eq 0) {$_} }
}

}
$text -replace '\s{2,}', ' '
}

$pattern = "(?im)<li>(.*?)<ul><li>(.*?)</li></ul></li>"

Foreach ($topic in $topics)
{
$url = "https://en.wikiquote.org/wiki/$Topic"

try
{
$content = Invoke-WebRequest -Uri $url -UseBasicParsing -ErrorAction Stop
}
catch [System.Net.WebException]
{
Write-Warning "Topic '$Topic' not found. Try a different one!"
return
}

$html = $content.Content.Replace("`n",'').Replace("`r",'')
[Regex]::Matches($html, $pattern) |
ForEach-Object {
[PSCustomObject]@{
Text = Remove-Tag $_.Groups[1].Value
Author = Remove-Tag $_.Groups[2].Value
Topic = $Topic
}
} | Get-Random -Count $Count
}
}



Get-Quote
Get-Quote -Topic Car
Get-Quote -Topic Jewel
Get-Quote -Topic PowerShell

PowerShell 技能连载 - 控制音量(静音和音量)

Ole Morten Didriksen 发现了一些 API 调用可以启用音量控制 (https://gist.github.com/oledid)。通过这种方法,用 PowerShell 控制音量静音、取消静音、设置音量就很容易了:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#requires -Version 2.0
Add-Type -TypeDefinition @'
using System.Runtime.InteropServices;
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioEndpointVolume {
// f(), g(), ... are unused COM method slots. Define these if you care
int f(); int g(); int h(); int i();
int SetMasterVolumeLevelScalar(float fLevel, System.Guid pguidEventContext);
int j();
int GetMasterVolumeLevelScalar(out float pfLevel);
int k(); int l(); int m(); int n();
int SetMute([MarshalAs(UnmanagedType.Bool)] bool bMute, System.Guid pguidEventContext);
int GetMute(out bool pbMute);
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice {
int Activate(ref System.Guid id, int clsCtx, int activationParams, out IAudioEndpointVolume aev);
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator {
int f(); // Unused
int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumeratorComObject { }
public class Audio {
static IAudioEndpointVolume Vol() {
var enumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
IMMDevice dev = null;
Marshal.ThrowExceptionForHR(enumerator.GetDefaultAudioEndpoint(/*eRender*/ 0, /*eMultimedia*/ 1, out dev));
IAudioEndpointVolume epv = null;
var epvid = typeof(IAudioEndpointVolume).GUID;
Marshal.ThrowExceptionForHR(dev.Activate(ref epvid, /*CLSCTX_ALL*/ 23, 0, out epv));
return epv;
}
public static float Volume {
get {float v = -1; Marshal.ThrowExceptionForHR(Vol().GetMasterVolumeLevelScalar(out v)); return v;}
set {Marshal.ThrowExceptionForHR(Vol().SetMasterVolumeLevelScalar(value, System.Guid.Empty));}
}
public static bool Mute {
get { bool mute; Marshal.ThrowExceptionForHR(Vol().GetMute(out mute)); return mute; }
set { Marshal.ThrowExceptionForHR(Vol().SetMute(value, System.Guid.Empty)); }
}
}
'@

# turn audio off
[Audio]::Mute = $true

# turn audio on
[Audio]::Mute = $false

# get volumne
[Audio]::Volume

# set volume (between 0 and 1)
[Audio]::Volume = 0.75

PowerShell 技能连载 - 增强版 Get-History 命令

当您在 PowerShell 中输入 h 命令,您可以看到在这个会话中输入的命令历史。受到 Pratek Singh 的启发 Powershell Get-History+ – Geekeefy,以下是一个灵活的 h+ 命令,它能够在网格界面窗口中显示历史纪录,并且支持选定历史记录。按住 CTRL 键可以选择多个项目。

Pratek 通过 Invoke-Expression 命令执行所有选中的项目。这可能有风险,并且它并不显示命令,所以您不知道执行了什么命令。所以在 h+ 中,我们把选中的项目复制到剪贴板中。这样,您可以将内容粘贴到需要的地方:可以粘贴到文件中,或是将它们粘贴回 PowerShell 来执行。将它们粘贴回 PowerShell 中之后,您还有机会查看这些命令,然后按 ENTER 键执行这些命令。

1
2
3
4
5
6
Function h+
{
Get-History |
Out-GridView -Title "Command History - press CTRL to select multiple - Selected commands copied to clipboard" -OutputMode Multiple |
ForEach-Object -Begin { [Text.StringBuilder]$sb = ""} -Process { $null = $sb.AppendLine($_.CommandLine) } -End { $sb.ToString() | clip }
}

只需要将 h+ 函数加入您的配置文件脚本中(可以通过 $profile 找到路径)这样它随时可以拿来使用。