PowerShell 技能连载 - 创建一大堆测试文件

适用于 PowerShell 所有版本

如果您需要对系统进行压力测试,或因为别的原因需要大量测试文件,以下是在瞬间创建大量文件(可以是大文件)的代码:

$Path = "$env:temp\hugefile.txt"
$Size = 200MB

$stream = New-Object System.IO.FileStream($Path, [System.IO.FileMode]::CreateNew)
$stream.Seek($Size, [System.IO.SeekOrigin]::Begin)
$stream.WriteByte(0)
$Stream.Close()

explorer.exe "/select,$Path"

PowerShell 技能连载 - 限制 String 的最大长度

适用于 PowerShell 所有版本

要限制输出的文本不会过长,您可以使用类似这样的的逻辑来缩短超过指定长度的文本:

if ($text.Length -gt $MaxLength)
{
  $text.Substring(0,$MaxLength) + '...'
}
else
{
  $text
}

PowerShell 技能连载 - 设置 AD 或 Windows 的权限

需要 ActiveDirectory 模块

我们之前已经演示了如何用 Get/Set-Acl 来读写文件和文件夹的权限。

实际上这两个 cmdlet 可以处理所有合法的 PowerShell 路径。说以您可以同样地在 Windows 注册表中使用相同的方法来读取、克隆和写入属性。

这个例子从一个注册表键中读取已有的安全信息,并应用到另一个键上:

# both Registry keys must exist
$KeyToCopySecurityFrom = 'HKLM:\Software\Key1'
$KeyToCopySecurityTo = 'HKLM:\Software\Key1'

$securityDescriptor = Get-Acl -Path $KeyToCopySecurityFrom
Set-Acl -Path $KeyToCopySecurityTo -AclObject $securityDescriptor

类似地,如果您从微软安装了 RSAT 工具并启用了 ActiveDirectory PowerShell 模块,您就可以使用它的 PowerShell 驱动器 AD: 来对 AD 对象做类似的操作,例如,从一个 OU 克隆委派权限到另一个 OU 上。

您现在可以根据需要读取、修改或重新应用如委派控制、防止意外删除等 Active Directory 特性。

Import-Module ActiveDirectory

# both OUs must exist
$OUtoCopyFrom = 'AD:\OU=Employees,DC=TRAINING,DC=POWERSHELL'
$OUtoCopyTo = 'AD:\OU=TestEmployees,DC=TRAINING,DC=POWERSHELL'

$securityDescriptor = Get-Acl -Path $OUtoCopyFrom
Set-Acl -Path $OUtoCopyTo -AclObject $securityDescriptor

您现在可以对任何 AD 对象通过这种方式读取和写入安全信息,包括 DNS 信息。您所需要的只是知道您想读写的对象的 LDAP 路径。

PowerShell 技能连载 - 捕获本地 EXE 的错误(第 2 部分)

适用于 PowerShell 所有版本

以下是检测控制台程序发出的错误的另一种方法:

$ErrorActionPreference = 'Continue'
$result = net.exe user UserDoesNotExist 2>&1

# $? is $false when something went wrong
if ($? -eq $false) {
    # read last error:
    $errMsg = $result.Exception.Message -join ','
    Write-Host "Something went wrong: $errMsg"
} else {
    Write-Host 'All is fine.'
}

请注意 $ErrorActionPreference 的用法:当它设置为‘Stop’时,错误将被转换为一个 .NET 异常。$ErrorActionPreference 的缺省设置是‘Continue’。通过这个设置,脚本可以通过 $err 获得错误信息。

如果最后一次调用失败了,内置的 $? 变量将会返回 $false。在这种情况下,代码将会返回一条错误信息(或者做其它事情,例如写日志文件)。

PowerShell 技能连载 - 用 SDDL 替换 NTFS 权限

适用于 PowerShell 所有版本

您可以通过 Get-Acl 命令将文件和文件夹的安全信息导出成 SDDL 格式(安全描述定义语言)的纯文本文件:

$FolderToRead = 'C:\folder1'

$securityDescriptor = Get-Acl -Path $FolderToRead
$securityDescriptor.GetSecurityDescriptorSddlForm('All')

您可以将 SDDL 通过管道输出到剪贴板,然后将它粘贴到另一个脚本中:

$FolderToRead = 'C:\folder1'

$securityDescriptor = Get-Acl -Path $FolderToRead
$securityDescriptor.GetSecurityDescriptorSddlForm('All') | clip.exe

类似这样将 SDDL 加入脚本中,例如(请注意 SDDL 总是只有一行,所以请不要添加换行符):

$sddl = 'O:S-1-5-21-2649034417-1209187175-3910605729-1000G:S-1-5-21-2649034417-1209187175-3910605729-513D:(A;ID;FA;;;BA)(A;OICIIOID;GA;;;BA)(A;ID;FA;;;SY)(A;OICIIOID;GA;;;SY)(A;OICIID;0x1200a9;;;BU)(A;ID;0x1301bf;;;AU)(A;OICIIOID;SDGXGWGR;;;AU)'


$FolderToConfigure = 'C:\folder2'

$securityDescriptor = Get-Acl -Path $FolderToConfigure
$securityDescriptor.SetSecurityDescriptorSddlForm($sddl)
Set-Acl -Path $FolderToConfigure -AclObject $securityDescriptor

将 SDDL 插入脚本之后,您就不再需要生成 SDDL 用的模板文件夹了。您可以将安全信息应用到其它文件系统对象中,例如设置基本 NTFS 权限,或先编辑 SDDL 再应用它。

为您提供一些启示,在域迁移的场景中,您可以比如创建一个转换表,用于将旧的 SID 转换为新的 SID。然后,将旧的 SID 替换成新的 SID,然后将记录下的安全信息克隆到一个新的(或测试的)域中。

PowerShell 技能连载 - 克隆 NTFS 权限

适用于 PowerShell 所有版本

以下代码从一个文件夹读取 NTFS 权限并将该设置应用到另外一个文件夹上。请注意两个文件夹都必须存在:

$FolderToCopyFrom = 'C:\folder1'
$FolderToCopyTo = 'C:\folder2'

$securityDescriptor = Get-Acl -Path $FolderToCopyFrom
Set-Acl -Path $FolderToCopyTo -AclObject $securityDescriptor

复制安全描述符操作可能需要管理员权限。请注意第二个文件夹的所有安全规则都会被第一个文件夹的安全信息覆盖。

PowerShell 技能连载 - 捕获本地 EXE 的错误(第 1 部分)

适用于 PowerShell 所有版本

当您运行本地控制台命令,例如 robocopy.exeipconfig.exe 或类似的命令时,您可以处理这些命令中抛出的错误:

try
{
    $current = $ErrorActionPreference
    $ErrorActionPreference = 'Stop'
    # this will cause an EXE command to emit an error
    # (replace with any console-based EXE command)
    net.exe user nonexistentUser 2>&1
    $ErrorActionPreference = $current
}
catch
{
   Write-Host ('Error occured: ' + $_.Exception.Message)
}

要捕获错误,您需要临时将 $ErrorActionPreference 设为“Stop”。另外,您需要用“2>&1”将错误信息重定向到输出控制台。

这么做完之后,例如 .NET 错误等错误就可以被 PowerShell 处理了。

PowerShell 技能连载 - 使用 WMI 继承

适用于 PowerShell 所有版本

WMI 类是彼此继承的,我们可以利用这个特性。例如这行代码:

PS> Get-WmiObject -Class Win32_Printer

它将返回通过 WMI 获取到的所有打印机。打印机是从更多的通用类继承的,这段代码可以显示继承树:

PS> Get-WmiObject -Class Win32_Printer | Select-Object -ExpandProperty __derivation -First 1
CIM_Printer
CIM_LogicalDevice
CIM_LogicalElement
CIM_ManagedSystemElement

所以如果您不只是对打印机感兴趣,而是对更多的硬件感兴趣,那么选择更通用的父类,例如 CIM_LogicalDevice。这行代码可以获取所有的硬件清单:

PS> Get-WmiObject -Class CIM_LogicalDevice

Manufacturer        Name                Status                       StatusInfo
------------        ----                ------                       ----------
Realtek             Realtek High Def... OK                                    3
                    Kona                OK
Intel Corporation   Intel(R) 8 Serie... OK
Intel Corporation   Intel(R) Wireles...
Microsoft           Microsoft Kernel...
                    ASIX AX88772B US...
Microsoft           Virtueller Micro...
Microsoft           Bluetooth-Gerät ...
Microsoft           Microsoft-ISATAP...
                    Microsoft-ISATAP...
Microsoft           Teredo Tunneling...
Microsoft           Von Microsoft ge...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    ASIX AX88772B US...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    Virtueller Micro...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
                    Microsoft-ISATAP...
-Virtual Battery 0- CRB Battery 0
(...)

这段代码将返回有关的类,这样的您就可以清晰地看到 WMI 如何调用您的硬件类型:

PS> Get-WmiObject -Class CIM_LogicalDevice |
  Group-Object -Property __Class -NoElement

Count Name
----- ----
    1 Win32_SoundDevice
    1 Win32_Battery
    1 Win32_IDEController
   20 Win32_NetworkAdapter
    1 Win32_PortableBattery
   10 Win32_Printer
    1 Win32_Processor
    2 Win32_DiskDrive
    7 Win32_DiskPartition
    1 Win32_Fan
    2 Win32_Keyboard
    5 Win32_LogicalDisk
    2 Win32_MappedLogicalDisk
    1 Win32_MemoryArray
    2 Win32_MemoryDevice
    2 Win32_PointingDevice
    1 Win32_SCSIController
    2 Win32_USBController
    6 Win32_USBHub
    5 Win32_Volume
    4 Win32_CacheMemory
    1 Win32_DesktopMonitor
    1 Win32_VideoController
    1 Win32_VoltageProbe
    1 Win32_MotherboardDevice
    8 Win32_Bus
  134 Win32_PnPEntity

It basically takes all the instances derived from CIM_LogicalDevice and groups them by “__Class” which is their real class name.
它基本上获取从 CIM_LogicalDevice 继承的所有实例并按照“__Class”分组。这是它们的真实类名。

PowerShell 技能连载 - 获取系统启动时间

适用于 PowerShell 所有版本

这个简单的函数可以返回当前系统的启动时间:

function Get-Uptime
{
  $millisec = [Environment]::TickCount
  [Timespan]::FromMilliseconds($millisec)
}

PowerShell 技能连载 - 查找进程所有者

适用于 PowerShell 所有版本

要查看某个进程的所有者以及有多少个实例在运行,请试试以下这段代码:

$ProcessName = 'explorer.exe'

(Get-WmiObject -Query "select * from Win32_Process where name='$ProcessName'").GetOwner().User

请注意:有许多办法能够查看当前登录的用户,并且根据您使用环境的不同,这里展示的方法可能有一定的局限性。它假设当前用户使用图形界面登录。由于在 server core 的机器上,只能运行非图形界面的进程,所以该脚本在该情况下不能检测连接到主机的用户名。

这个例子返回这台机器上所有“explorer.exe”进程的所有者。如果您有管理员权限并且远程进行此操作,那么该用户列表将类似已交互式登录的用户,因为每个桌面用户都创建了一个 explorer 进程。

当加入一个 Sort-Object 命令之后,您就可以轻松地排除重复项:

$ProcessName = 'explorer.exe'

(Get-WmiObject –Query "select * from Win32_Process where name='$ProcessName'").GetOwner().User |
  Sort-Object -Unique

如果改变进程名,您会发现其它有趣的东西。这段代码将列出当前通过 PowerShell 远程操作访问您机器的所有用户:

$ProcessName = 'wsmprovhost.exe'

try
{

  (Get-WmiObject -Query "select * from Win32_Process where name='$ProcessName'").GetOwner().User |
  Sort-Object -Unique
}
catch
{
  Write-Warning "No user found."
}