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 找到路径)这样它随时可以拿来使用。

PowerShell 技能连载 - 获取 MAC 制造商列表

Prateek Singh 贡献了一个干净的,CSV 格式的的 MAC 厂商列表。这个列表可以在他的博客 Get-MACVendor using Powershell – Geekeefy 找到。在通过 MAC 地址确定网络设备的厂商时,这个列表十分有用。

您可以用 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
#requires -Version 3.0

$url = 'http://goo.gl/VG9XdU'
$target = "$home\Documents\macvendor.csv"
Invoke-WebRequest -Uri $url -UseBasicParsing -OutFile $target

$content = Import-Csv -Path $target
$content | Out-GridView


With this awesome list, you can now take the first three numbers of any MAC address and find its manufacturer. Here is a simple sample implementation taking the information from Get-NetAdapter, and adding Manufacturer info:

#requires -Modules NetAdapter
#requires -Version 4.0

$url = 'https://raw.githubusercontent.com/PrateekKumarSingh/PowershellScrapy/master/MACManufacturers/MAC_Manufacturer_Reference.csv'
$target = "$home\Documents\macvendor.csv"
$exists = Test-Path -Path $target
if (!$exists)
{
Invoke-WebRequest -Uri $url -UseBasicParsing -OutFile $target
}

$content = Import-Csv -Path $target

Get-NetAdapter |
ForEach-Object {
$macString = $_.MacAddress.SubString(0, 8).Replace('-','')
$manufacturer = $content.
Where{$_.Assignment -like "*$macString*"}.
Foreach{$_.ManufacturerName}

$_ |
Add-Member -MemberType NoteProperty -Name Manufacturer -Value $manufacturer[0] -PassThru |
Select-Object -Property Name, Mac*, Manufacturer
}