PowerShell 技能连载 - 谁在使用网络资源?

假设您拥有管理员权限,您可以使用一个简单的 WMI 类来检测某人是否正在通过网络访问您的资源:

PS> Get-WmiObject -Class Win32_ServerConnection |
Select-Object -Property ComputerName, ConnectionID, UserName, ShareName

这个操作也可以远程执行:只需要为 Get-WmiObject 命令增加 -ComputerName 参数即可查看谁在访问该远程计算机上的共享资源。需要拥有目标计算机的管理员权限才可以进行远程操作。

PowerShell 技能连载 - 禁止更新后自动重启

您是否厌烦了 Windows 安装了一些更新后导致的非计划中的重启?

和其它情况类似,您可以通过组策略控制重启,而且多数组策略设置只是注册表键而已。以下是一个通过设置注册表键来实现控制安装更新后的重启设置的示例脚本:

$code =
{
  $key = 'HKLM:\Software\Policies\Microsoft\Windows\WindowsUpdate\AU'
  $name = 'NoAutoRebootWithLoggedOnUsers'
  $type = 'DWord'
  $value = 1

  if (!(Test-Path -Path $key))
  {
    $null = New-Item -Path $key -Force
  }
  Set-ItemProperty -Path $key -Name $name -Value $value -Type $type
}

Start-Process -FilePath powershell.exe -ArgumentList $code -Verb runas -WorkingDirectory c:\

请注意该脚本如何操作注册表:它实际上是通过另一个 PowerShell 实例间接执行的。第二个实例是通过 Start-Process 启动的,而“-Verb Runas”确保了以管理员身份运行这段代码。

如果您当前没有管理员权限,那么会弹出提升权限的对话框供您选择使用管理员权限,或是如果您的账户没有管理员权限的时候要求您选择一个有权限的账户。

PowerShell 技能连载 - 移除空白(和换行)

您也许知道每个 string 对象都有个 Trim() 方法可以删除该字符串开头和结尾的空白:

$text = '    Hello     '
$text.Trim()

一个鲜为人知的事实是,Trim() 也会删掉开头和结尾的换行:

$text = '

 Hello


 '
$text.Trim()

如果您需要,您可以控制 Trim() 函数吃掉的字符。

这个例子删除空格、点号、减号和换行:

$text = '

 ... Hello

 ...---
 '
$text.Trim(" .-`t`n`r")

PowerShell 技能连载 - 从 PowerShell 脚本中接收错误返回值

以下是一个演示 PowerShell 如何返回一个数值型状态码给调用者的简单脚本:

$exitcode = 123

$p = Start-Process -FilePath powershell -ArgumentList "-command get-process; exit $exitcode" -PassThru
Wait-Process -Id $p.Id

'External Script ended with exit code ' + $p.ExitCode

如果您在 PowerShell 中直接调用该脚本(不使用 Start-Process),那么数值型返回值会被赋给 $LASTEXITCODE

$exitcode = 199

powershell.exe "get-process; exit $exitcode"

'External Script ended with exit code ' + $LASTEXITCODE

如果您从一个批处理文件或是 VBScript 中运行一段 PowerShell 脚本,该数值型返回值将会赋给 %ERRORLEVEL% 环境变量,好比 PowerShell 是一个控制台应用程序一样——实际上 powershell.exe 确实是。

PowerShell 技能连载 - 为什么“exit”将会关掉 PowerShell

某些时候,我们会误会“exit”语句的工作方式。以下是一个例子:

function abc
{
  'Start'

  exit 100

  'Done'
}

abc

当您运行这个脚本时,abc 函数会被调用,然后退出。您会见到“Start”提示,但见不到“Done”提示,并且 $LASTEXITCODE 变量的值为 100。但这是实际情况吗?

当您在交互式的 PowerShell 控制台以交互式的方式运行 abc 方式时,该函数仍然退出了,不过这次,PowerShell 也被关闭了。为什么呢?

“Exit”在调用者作用域中退出代码。当您运行一个脚本时,该脚本退出后 PowerShell 仍然继续运行。当您以交互式的方式执行该函数时,交互式的 PowerShell 作为全局作用域退出了,而且由于不存在更高层的作用域了,所以 PowerShell 关闭了。

要更明显一点体现这个观点,我们在上述示例脚本中增加一点内容:

function abc
{
  'Start'

  exit 100

  'Done'
}

'Function starts'
abc
'Function ends'

如您所发现的,“exit”实际上并不是退出 abc 函数,而是退出整个脚本。所以您既见不到“Done”字样也见不到“Function ends”字样。

所以请慎用“exit”语句!它只能用在退出一个脚本并将控制权交还给调用者的时候。

PowerShell 技能连载 - 理解 break、continue、return 和 exit 语句

您是否十分熟悉“break”、“continue”、“return”和“exit”的用法?这些是十分有用的语言概念,以下是一个演示它们不同之处的测试函数:

'Starting'

function Test-Function {
    $fishtank = 1..10

    Foreach ($fish in $fishtank)
    {
        if ($fish -eq 7)
        {
            break      # <- abort loop
            #continue  # <- skip just this iteration, but continue loop
            #return    # <- abort code, and continue in caller scope
            #exit      # <- abort code at caller scope
        }

        "fishing fish #$fish"

    }
    'Done.'
}

Test-Function


'Script done!'

只需要去掉某个关键词的注释并运行脚本,就可以观察循环的执行结果。

PowerShell 技能连载 - 检测危险的 NTFS 权限

以下是一个查找潜在危险的 NTFS 权限的快速简单的方法。这段脚本检测所有 $pathsToCheck 的文件夹并且汇报错有具有 $dangerousBitMask 中定义的文件系统标志的安全存取控制项(译者注:也就是“路径”)。

在这个例子中,该脚本从您的 %PATH% 环境变量中得到所有查找到的路径。这些路径是高风险的,需要用 NTFS 权限来保护,只能由 Administrators 和 system 拥有写权限。

软件安装程序常常将它们自身加入环境变量而没有正确地保护它们所加入的文件夹权限。这将增加安全风险。以下脚本将检查这些地方并找出哪些有潜在危险的 NTFS 存取权限供您做决定。

# list of paths to check for dangerous NTFS permissions
$pathsToCheck = $env:Path -split ';'

# these are the bits to watch for
# if *any* one of these is set, the folder is reported
$dangerousBitsMask = '011010000000101010110'
$dangerousBits = [Convert]::ToInt64($dangerousBitsMask, 2)

# check all paths...
$pathsToCheck |
ForEach-Object {
  $path = $_
  # ...get NTFS security descriptor...
  $acl = Get-Acl -Path  $path
  # ...check for any "dangerous" access right
  $acl.Access |
  Where-Object { $_.AccessControlType -eq 'Allow' } |
  Where-Object { ($_.FileSystemRights -band $dangerousBits) -ne 0 } |
  ForEach-Object {
    # ...append path information, and display filesystem rights as bitmask
    $ace = $_
    $bitmask = ('0' * 64) + [Convert]::toString([int]$ace.FileSystemRights, 2)
    $bitmask = $bitmask.Substring($bitmask.length - 64)
    $ace | Add-Member -MemberType NoteProperty -Name Path -Value $path -PassThru | Add-Member -MemberType NoteProperty -Name Rights -Value $bitmask -PassThru
  }
} |
Sort-Object -Property IdentityReference |
Select-Object -Property IdentityReference, Path, Rights, FileSystemRights |
Out-GridView

PowerShell 技能连载 - 获取 IP 地址的地理信息

您希望知道某个公网 IP 地址位于什么地方吗?假设您有 Internet 连接,您可以查询公共信息服务来获得。

这个例子将获取某个 IP 地址的地理信息。请确认您将示例 IP 地址替换为了实际存在的公网 IP 地址。请打开类似 https://www.whatismyip.com/ 这样的网站来查看您自己的 IP 地址。如果您使用的是一个内网 IP 地址,该 WEB 服务将无法准确地报告地理信息数据。

#requires -Version 3

$ipaddress = '93.212.237.11'
$infoService = "http://freegeoip.net/xml/$ipaddress"

$geoip = Invoke-RestMethod -Method Get -URI $infoService

$geoip.Response

PowerShell 技能连载 - 获取当前 IP 地址

以下是一段您计算机绑定的所有 IP 地址的代码:

#requires -Version 1


$ipaddress = [System.Net.DNS]::GetHostByName($null)
Foreach ($ip in $ipaddress.AddressList)
{
  $ip.IPAddressToString
}

如果您将 $null 替换为主机名(例如“_server123_”),就可以获取对应计算机绑定的 IP 地址。

如果您只需要获取 IPv4 地址,请试试这段代码:

#requires -Version 1


$ipaddress = [System.Net.DNS]::GetHostByName($null)
foreach($ip in $ipaddress.AddressList)
{
  if ($ip.AddressFamily -eq 'InterNetwork')
  {
    $ip.IPAddressToString
  }
}

PowerShell 技能连载 - 验证域凭据

要通过当前的域验证凭据(用户名和密码),您可以使用这段代码:

#requires -Version 1

$username = 'test\user'
$password = 'topSecret'

$root = "LDAP://" + ([ADSI]"").distinguishedName
$Domain = New-Object System.DirectoryServices.DirectoryEntry($root, $username, $password)

if ($Domain.Name -eq $null)
{
  Write-Warning 'Credentials incorrect, or computer is not a domain member.'
}
else
{
  Write-Host 'Credentials accepted.'
}

总的来说,该脚本首先确认当前域名,然后用提供的凭据来获取根元素。

如果该操作成功完成,说明凭据时合法的。如果失败,说明凭据是无效的,或者该计算机根本没有加入域。