PowerShell 技能连载 - 获取远程主机的系统信息

在上一个技巧当中您已学到如何用 systeminfo.exe 获取丰富的系统资料。systeminfo.exe 内置了远程的功能,所以如果您拥有了适当的权限,您可以获取远程主机的系统信息。

以下是一个简单的函数:

function Get-SystemInfo
{
  param($ComputerName = $env:ComputerName)

      $header = 'Hostname','OSName','OSVersion','OSManufacturer','OSConfig','Buildtype',`'RegisteredOwner','RegisteredOrganization','ProductID','InstallDate','StartTime','Manufacturer',`'Model','Type','Processor','BIOSVersion','WindowsFolder','SystemFolder','StartDevice','Culture',`'UICulture','TimeZone','PhysicalMemory','AvailablePhysicalMemory','MaxVirtualMemory',`'AvailableVirtualMemory','UsedVirtualMemory','PagingFile','Domain','LogonServer','Hotfix',`'NetworkAdapter'
      systeminfo.exe /FO CSV /S $ComputerName |
            Select-Object -Skip 1 |
            ConvertFrom-CSV -Header $header
}

以下是简单的调用示例:

PowerShell 技能连载 - 获取系统信息

PowerShell 和现有的控制台程序可以很好地共存。一个最有用的是 systeminfo.exe,它可以收集各种有用的系统信息。通过导入 systeminfo.exe 提供的 CSV 信息,PowerShell 可以将文本信息转化为对象:

$header = 'Hostname','OSName','OSVersion','OSManufacturer','OSConfig','Buildtype',`'RegisteredOwner','RegisteredOrganization','ProductID','InstallDate','StartTime','Manufacturer',`'Model','Type','Processor','BIOSVersion','WindowsFolder','SystemFolder','StartDevice','Culture',`'UICulture','TimeZone','PhysicalMemory','AvailablePhysicalMemory','MaxVirtualMemory',`'AvailableVirtualMemory','UsedVirtualMemory','PagingFile','Domain','LogonServer','Hotfix',`'NetworkAdapter'

systeminfo.exe /FO CSV |   Select-Object -Skip 1 |   ConvertFrom-CSV -Header $header

当您运行这段代码时,它将停顿数秒钟,以供 systeminfo.exe 收集信息。然后,您将会获得大量的信息:

请注意 $header:这个变量定义了属性名称,并且用自定义的列表替换了缺省的表头。所以,无论操作系统是哪种语言的,这些表头永远是相同的。

您还可以将这些信息存储在一个变量中,然后分别存取其中的信息:

$header = 'Hostname','OSName','OSVersion','OSManufacturer','OSConfig','Buildtype',`'RegisteredOwner','RegisteredOrganization','ProductID','InstallDate','StartTime','Manufacturer',`'Model','Type','Processor','BIOSVersion','WindowsFolder','SystemFolder','StartDevice','Culture',`'UICulture','TimeZone','PhysicalMemory','AvailablePhysicalMemory','MaxVirtualMemory',`'AvailableVirtualMemory','UsedVirtualMemory','PagingFile','Domain','LogonServer','Hotfix',`'NetworkAdapter'

$result = systeminfo.exe /FO CSV |
  Select-Object -Skip 1 |
  ConvertFrom-CSV -Header $header

PowerShell 技能连载 - 远程启动服务

由于 Start-Service 命令没有 -ComputerName 参数,所以您无法简单地远程启动一个服务。然而您可以在一个 PowerShell 远程管理会话中运行 Start-Service 命令。在某些场景下,一个更简单的方法是使用 Set-Service 命令。以下代码可以在名为 Server12 的服务器上远程启动 Spooler 服务:

Set-Service -Name Spooler -Status Running -ComputerName Server12

不幸的是,这个命令没有 -Force 开关。所以虽然您可以简单地启动服务,但您可能无法用这种方式停止它们。当一个服务依赖于另一个服务时,它必须使用“强制”的方式来停止。

PowerShell 技能连载 - 使用 ICACLS 提高文件夹安全性

在 PowerShell 系统中,控制台程序也是相同的“一等公民”。在这个例子中,New-Folder 函数使用 icacls.exe 来设置新建文件夹的权限:

function New-Folder
{
  param
  (
    $Path,
    $Username
  )

  If ( (Test-Path -Path $path) -eq $false )
  {
    New-Item $path -Type Directory | Out-Null
  }

  icacls $path /inheritance:r /grant '*S-1-5-32-544:(OI)(CI)R' ('{0}:(OI)(CI)F' -f $username)
}

New-Folder 函数将创建一个新文件夹(如果它不存在),然后使用 icacls.exe 来禁止继承、允许 Administrators 组读取以及赋予指定用户完全控制权限。

PowerShell 技能连载 - 降低 PowerShell 进程优先级

当您运行一个 PowerShell 任务时,默认情况下它的优先级是 Normal。并且如果您脚本所做的事十分消耗 CPU 的话,您机器的性能可能会受影响。

要避免这个现象,您可以将您的 PowerShell 进程设置为更低的优先级,这样它仅在 CPU 负载允许的情况下运行。这可以确保您的 PowerShell 任务不会影响其它任务的性能。

这个例子将优先级设为“Below Normal”。您也可以将它设置为“Idle”,那样您的 PowerShell 脚本仅当机器没有别的事做时才会运行。

$process = Get-Process -Id $pid
$process.PriorityClass = 'BelowNormal'

译者注:可能的 PriorityClass 值为 NormalIdleHighRealTimeBelowNormalAboveNormal
要找到明确的文档比较困难,但是有一个取巧的办法:故意打错。比如说我们可以打成 $process.PriorityClass = 'trudellic',运行以后提示:

Exception setting "PriorityClass": "Cannot convert value "trudellic" to type
"System.Diagnostics.ProcessPriorityClass".
Error: "Unable to match the identifier name trudellic to a valid enumerator name.

这时候可用的值在错误提示中就暴露出来了 :-)

PowerShell 技能连载 - PowerShell 远程管理和大尺寸令牌问题

Kerberos 令牌大小取决于用户组成员的数量。在某些重度使用组成员的企业环境中,令牌的大小可能会溢出 PowerShell 远程管理的限制。在这些情况下,PowerShell 远程管理操作会失败,提示一句模糊的信息。

要使用 PowerShell 远程管理,您可以设置两个注册表值,并且增加令牌的允许尺寸:

#Source: http://www.miru.ch/how-the-kerberos-token-size-can-affect-winrm-and-other-kerberos-based-services/
New-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters `-Name "MaxFieldLength" -Value 65335 -PropertyType "DWORD"
New-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters `-Name "MaxRequestBytes" -Value 40000 -PropertyType "DWORD"

PowerShell 技能连载 - 获取最新的地震信息

现代社会中,所有东西都是彼此相连的。PowerShell 可以从 web service 中获取公共数据。以下仅仅一行代码就可以为您获取最新检测到的地震以及它们的震级:

Invoke-RestMethod -URI "http://www.seismi.org/api/eqs" |
  Select-Object -First 30 -ExpandProperty Earthquakes |
  Out-GridView

PowerShell 技能连载 - 从多个事件日志中获取错误事件

Get-EventLog 命令每次只能读取一个事件日志。然而如果您希望从多个事件日志中读取事件,您可以传入数组信息:

$events = @(Get-EventLog -LogName System -EntryType Error)
$events += Get-EventLog -LogName Application -EntryType Error

$events

在这些例子中,使用 WMI 来查询可能会更简单一些——它可以一次性查询任意多个系统日志。

以下代码将从应用程序和系统日志中获取前 100 条错误日志(指的是总计 100 条,所以如果前 100 条错误都是应用程序日志,则当然不会包括系统错误):

Get-WmiObject -Class Win32_NTLogEvent -Filter 'Type="Error" and (LogFile="System" or LogFile="Application")' |
  Select-Object -First 100 -Property TimeGenerated, LogFile, EventCode, Message

当您将 Get-WmiObject 换成 Get-CimInstance(PowerShell 3.0 新增的命令),那么诡异的 WMI 日期格式将会被自动转换为普通的日期和时间:

Get-CimInstance -Class Win32_NTLogEvent -Filter 'Type="Error" and (LogFile="System" or LogFile="Application")' |
  Select-Object -First 100 -Property TimeGenerated, LogFile, EventCode, Message

PowerShell 技能连载 - 有序哈希表以及更改顺序

有序哈希表是在 PowerShell 3.0 中增加的新特性,它在创建新对象的时候十分有用。和常规的哈希表不同,有序哈希表保存了您添加键时的顺序,您还可以控制这些键转化为对象属性时的顺序。以下是一个例子:

$hashtable = [Ordered]@{}
$hashtable.Name = 'Tobias'
$hashtable.ID = 12
$hashtable.Location = 'Germany'

New-Object -TypeName PSObject -Property $hashtable

这段代码创建了一个对象,它的属性定义严格按照它们指定时的先后顺序排列。

那么如果您希望不在尾部,例如在列表的头部增加一个属性,要怎么做呢?

$hashtable = [Ordered]@{}
$hashtable.Name = 'Tobias'
$hashtable.ID = 12
$hashtable.Location = 'Germany'
$hashtable.Insert(0, 'Position', 'CSA')

New-Object -TypeName PSObject -Property $hashtable

PowerShell 技能连载 - 获取昨天午夜的日期值

当您了解了每个 DateTime 对象支持 Add...() 方法之后,获取相对日期(例如昨天或下周)就十分容易了。以下代码可以获取昨天的时间值:

$today = Get-Date
$yesterday = $today.AddDays(-1)
$yesterday

$yesterday 的值是当前时间之前 24 小时整的值。那么如果您希望得到昨天特定时刻的值,比如说昨天午夜呢?

如果您希望得到今天午夜的值,那么十分简单:

$todayMidnight = Get-Date -Hour 0 -Minute 0 -Second 0
$todayMidnight

如果您希望得到另一天的该时间值,那么再次使用 Get-Date 来修改时间值。以代码获取昨天午夜的时间值:

$today = Get-Date
$yesterday = $today.AddDays(-1)
$yesterday | Get-Date -Hour 0 -Minute 0 -Second 0

译者注:如果您只是需要获取昨天午夜的日期值,还可以有其它方法。如:(Get-Date).AddDays(-1).Date[System.DateTime]::Today.Subtract([System.TimeSpan]::FromDays(1))