PowerShell 技能连载 - 从网站上下载图片

有许多有趣的网站,其中一个是 www.metabene.de (至少面向德国访访客),有 33 页内容,艺术家展示了他的绘画,并提供免费下载(只允许私人使用·私人使用)。

在类似这种情况中,PowerShell 可以帮助您将手动从网站下载图片的操作自动化。在 PowerShell 3.0 中,引入了一个称为 Invoke-WebRequest 的“PowerShell 浏览器”,它能够将人类在一个真实浏览器中操作的大多数事情自动化。

当您运行这段脚本时,它访问所有的 33 个网页,检查所有的图片链接,并将它们保存到硬盘上:

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
# open destination folder (and create it if needed)
$folder = 'c:\drawings'
$exists = Test-Path -Path $folder
if (!$exists) { $null = New-Item -Path $folder -ItemType Directory }
explorer $folder

# walk all 33 web pages that www.metabene.de offers
1..33 | ForEach-Object {
$url = "http://www.metabene.de/galerie/page/$_"

# navigate to website...
$webpage = Invoke-WebRequest -Uri $url -UseBasicParsing

# take sources of all images on this website...
$webpage.Images.src |
Where-Object {
# take only images that were uploaded to this blog
$_ -like '*/uploads/*'
}
} |
ForEach-Object {
# get filename of URL
$filename = $_.Split('/')[-1]
# create local file name
$destination= Join-Path -Path $Folder -ChildPath $filename
# download pictures
Invoke-WebRequest -Uri $url -OutFile $destination
}

PowerShell 技能连载 - Cmelet 错误报告的简单策略

在 PowerShell 中,您可以创建复杂的错误处理代码,但有些时候您可能只是想知道出了什么错并且把它记录下来。不需要额外的技能。

以下是两个可能会遇到错误的 cmdlet。当这些 cmdlet 执行完成时,您将能通过 $data1$data2 获得它们的执行结果,并在控制台中见到许多红色的错误信息:

1
2
$data1 = Get-ChildItem -Path c:\windows -Filter *.ps1 -Recurse
$data2 = Get-Process -FileVersionInfo

现在看看这个实验:

1
2
$data1 = Get-ChildItem -Path c:\windows -Filter *.ps1 -Recurse -ErrorAction SilentlyContinue -ErrorVariable errorList
$data2 = Get-Process -FileVersionInfo -ErrorAction SilentlyContinue -ErrorVariable +errorList

要禁止错误输出并同时将红色的错误信息写入自定义的错误变量 $errorList 并不需要做太多工作。请注意 -ErrorVariable 参数接受的是一个变量名(不含 “$“ 前缀)。并请注意在这个变量名前添加 “+“ 前缀将能把错误信息附加到变量中,而不是替换变量的值。

现在,两个 cmdlet 运行起来都看不到错误信息了。最后,您可以容易地在 $errorList 中容易地分析错误信息,例如用 Out-File 将它们写入某些错误日志文件:

1
2
3
$issues = $errorList.CategoryInfo | Select-Object -Property Category, TargetName
$issues
$issues | Out-File -FilePath $home\Desktop\report.txt -Append

PowerShell 技能连载 - 检查文件名的非法字符

文件的命名是十分敏感的,不能包含某些特定的保留字符。要验证文件名并确保这些字符是合法的,以下对昨天的脚本(检查文件系统路径)做了一点改进。这个脚本检查文件名的合法性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# check path:
$filenameToCheck = 'testfile:?.txt'

# get invalid characters and escape them for use with RegEx
$illegal =[Regex]::Escape(-join [System.Io.Path]::GetInvalidFileNameChars())
$pattern = "[$illegal]"

# find illegal characters
$invalid = [regex]::Matches($filenameToCheck, $pattern, 'IgnoreCase').Value | Sort-Object -Unique

$hasInvalid = $invalid -ne $null
if ($hasInvalid)
{
"Do not use these characters in file names: $invalid"
}
else
{
'OK!'
}

结果如下:

Do not use these characters in file names: : ?

PowerShell 技能连载 - 查找文件路径中的非法字符(基于文件系统)

之前我们演示了如何使用简易的基于正则表达式的方法来查找字符串中的非法字符。我们鼓励您将这种策略运用到各种需要验证的字符串中。

如果您希望检测文件系统中的非法字符,以下是一个简单的适配:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# check path:
$pathToCheck = 'c:\test\<somefolder>\f|le.txt'

# get invalid characters and escape them for use with RegEx
$illegal =[Regex]::Escape(-join [System.Io.Path]::GetInvalidPathChars())
$pattern = "[$illegal]"

# find illegal characters
$invalid = [regex]::Matches($pathToCheck, $pattern, 'IgnoreCase').Value | Sort-Object -Unique

$hasInvalid = $invalid -ne $null
if ($hasInvalid)
{
"Do not use these characters in paths: $invalid"
}
else
{
'OK!'
}

非法的字符是从 GetInvalidPathChars() 中提取的并且用正则表达式转换为转义字符串。这个列表放在方括号中,所以当其中任一字符匹配成功时,RegEx 将能够报告匹配结果。

以下是结果:

Do not use these characters in paths: | < >

PowerShell 技能连载 - 查找多个非法字符

之前发,我们演示了如何用 -match 操作符来查找一段文本中的非法字符。不过,-match 操作符只能查找第一个匹配项。要列出字符串中的所有非法字符,请使用这种方法:

1
2
3
4
5
6
7
8
9
10
11
# some email address
$mail = 'THOMAS.börßen_senbÖrg@test.com'
# list of legal characters, inverted by "^"
$pattern = '[^a-z0-9\.@]'

# find all matches, case-insensitive
$allMatch = [regex]::Matches($mail, $pattern, 'IgnoreCase')
# create list of invalid characters
$invalid = $allMatch.Value | Sort-Object -Unique

'Illegal characters found: {0}' -f ($invalid -join ', ')

结果看起来如下:

Illegal characters found: _, ö, ß

PowerShell 技能连载 - 检查电子邮件地址(或其它文本)中的非法字符

这是一个清晰的检测和验证数据的快捷方法。假设您需要验证一个电子邮件地址是否包含非法字符:

1
2
3
4
5
6
7
8
9
10
11
12
13
# some email address
$mail = 'thomas.börsenberg@test.com'
# list of allowed characters
$pattern = '[^a-z0-9\.@]'

if ($mail -match $pattern)
{
('Invalid character in email address: {0}' -f $matches[0])
}
else
{
'Email address is good.'
}

这段代码使用了正则表达式。正则表达式列出所有合法的字符(从 a 到 z,以及某些特殊字符)。在前面加上“^”,列表的含义发生反转,代表所有非法字符。如果找到至少一个字符,则返回第一个非法字符。

Invalid character in email address: ö

PowerShell 技能连载 - PowerShell Remoting and HTTP 403 Error

如果您在使用 PowerShell 远程操作时遇到“HTTP 403”错误,一个潜在的原因可能是代理服务器影响了请求。

不过,要禁用代理服务器很容易。只需要在您的呼叫中增加一个 session 选项并且将 ProxyAccessType 设置为“NoProxyServer”即可:

1
2
3
4
5
6
7
8
# you are connecting to this computer
# the computer in $destinationcomputer needs to have
# PowerShell remoting enabled
$DestinationComputer = 'server12'

$option = New-PSSessionOption -ProxyAccessType NoProxyServer

Invoke-Command -ScriptBlock { "Connected to $env:computername" } -ComputerName $DestinationComputer -SessionOption $option

PowerShell 技能连载 - 在单侧启用 CredSSP

如之前所示,CredSSP 可以用在远程代码上,避免二次连接问题。不过,要使用 CredSSP 验证方式您得在客户端和服务端分别设置,才能使它们彼此信任。

这并不意味着您必须物理接触那台服务器。如果您想在您的计算机和任何一台服务器(假设那台服务器上启用了 PowerShell 远程连接)建立一个 CredSSP 信任关系,只需要做这些:

1
2
3
4
5
6
7
8
#requires -Version 2.0 -RunAsAdministrator

# this is the server you want to communicate with using CredSSP
# the server needs to have PowerShell remoting enabled already
$Server = 'NameOfServer'

Enable-WSManCredSSP -Role Client -DelegateComputer $Server -Force
Invoke-Command { Enable-WSManCredSSP -Role Server } -ComputerName $Server

如您所见,Enable-WSManCredSSP 可以远程执行。

PowerShell 技能连载 - 用 CredSSP 解决二次远程连接

在钱一个技能中我们演示了当您的远程代码试图通过第三方身份认证时会遇到的二次远程连接问题。

在您在客户端和服务端之间创建了一个可信的连接之后,可以传递您的凭据(只需要做一次,并且需要管理员权限)。

在客户端,运行这段代码:

1
Enable-WSManCredSSP -Role Client -DelegateComputer nameOfServer

在服务端,运行这段代码:

1
Enable-WSManCredSSP -Role Server

现在将 PowerShell 代码从客户端发送到服务端并执行时,服务端能够将您的凭据送给第三方验证通过,所以远程代码可以通过文件服务器的身份认证并访问共享文件夹:

1
2
3
4
5
6
7
8
#requires -Version 3.0

$code =
{
Get-ChildItem -Path \\fls01\#TRAIN1\PowerShell\Class
}

Invoke-Command -Authentication Credssp -ScriptBlock $code -ComputerName nameOfServer -Credential myCompany\myName

请注意当您使用 CredSSP 验证时,您必须提交显式的凭据(用 -Credential)且无法通过 Kerberos 透明传输当前的身份。

PowerShell 技能连载 - 理解二次远程连接问题

当您用 Invoke-Command 远程执行 PowerShell 代码时,您的凭据将会锁定在首个连接的机器上。

PowerShell 远程连接默认情况下不会传递您的凭据,不会使用您的凭据来登录别的系统。这听起来是个好主意,不过在某些情况也严重限制了您的代码。

这是一个典型的错误代码:

1
2
3
4
5
6
$code =
{
Get-ChildItem -Path \\fls01\#TRAIN1\PowerShell\Class
}

Invoke-Command -ScriptBlock $code -ComputerName server1

这段代码试图在远程访问一个文件共享。但是即便您有权限访问该共享,这段远程代码也无法使用您的身份进行第三方验证(在这个例子中是文件服务器)。