實戰2: 使用PowerShell創建一個更好的清單工具

PowerShell : 創建一個更好的清單工具


在以前的專欄裏,我創建了一個函數,可以收集到多臺遠程計算機中的補丁包清單信息,儘管這個工具已經很實用了,但是我更加關注開發這個工具中所用到的流程方法。在這裏,我會向大家演示這些流程方法,這樣你就可以更加清楚如何去創建自己的函數了
在前面我所創建的函數大概是如下
Function Get-SPInventory {
    PROCESS {
$wmi = Get-WmiObject Win32_OperatingSystem –comp $_ | select CSName,BuildNumber, ServicePackMajorVersion
Write-Output $wmi
    }
}
你可以這樣使用它
Get-Content c:\computernames.txt | Get-SPInventory
如果文本文件中每行就是每個計算機名的話,這函數很有效


對像總是一樣的

問題是,我的函數是從Win32_OperatingSystem 類讀取信息,並且簡單的輸出顯示該對象。Win32_OperatingSystem 類,就像其他對象一樣所包含的數據是固定的--- 他沒有並且不可能有包含BIOS序列號信息,如果我只是單純輸出Win32_OperatingSystem類對象,我是不可能得到序列號的。
不管我們怎麼查找WMI,我們肯定是找不到一個單獨的對象,它既包含了BIOS序列號信息,也包含了補丁包信息。因此,我的函數不能只是顯示一個WMI類,我需要顯示一個自定義對象,包含我所有數據的對象。
要創建一個新的、空白沒有屬性的對象,我只是運行
$obj = New-Object PSObject

這個新對象是存放在變量$obj中的,並且我可以往裏面添加任何我需要的數據。一旦我把所有所需的數據加入進去,他就變成了我的函數的輸出
在我的函數中,我從Win32_OperatingSystem中得到信息,並且保存在變量$Wmi中。就如察看$Wmi屬性那樣簡單,我們可以如下的使用來得到想要的信息,比如,有關BuildNumber屬性,我可以
$wmi.BuildNumber

爲了給我的自定義對象加入屬性,我需要對對象使用管道(在變量$obj中)輸出到Add-Memeber,我需要告訴Add-Member 屬性類型是什麼(一般是NoteProperty),屬性名稱,屬性值,比如
$obj | Add-Member NoteProperty BuildNumber ($wmi.BuildNumber)

同樣也適用於計算機名和補丁包版本
$obj | Add-Member NotePropertyCSName ($wmi.CSName)
$obj | Add-Member NotePropertySPVersion ($wmi.ServicePackMajorVersion)

依此我們可以創建新的函數(請察看圖片1),請注意我更改了Write-Output行來顯示我定製化對象,而不是現實原來的$wmi對象
clip_p_w_picpath001 圖1 自定義對象
Function Get-SPInventory {
PROCESS {
$wmi = Get-WmiObject Win32_OperatingSystem -comp $_ | Select CSName,BuildNumber,ServicePackMajorVersion
$obj = New-Object PSObject
$obj | Add-Member NoteProperty BuildNumber ($wmi.BuildNumber)
$obj | Add-Member NoteProperty CSName ($wmi.CSName)
$obj | Add-Member NoteProperty SPVersion ($wmi.ServicePackMajorVersion)
Write-Output $obj
}
}
現在我需要拿到BIOS序列號信息,通過搜索我們知道它是Win32_BIOS類,其中有一個序列號Serial Number的屬性(參考圖片2),所以我只要見到的查詢Win32_BIOS類,把序列號屬性加到我定製對象中,做完後,你可以看到修改後的函數(圖片3)
clip_p_w_picpath002
圖2 Win32_Bios 文檔頁面
clip_p_w_picpath001[1] 圖3:包含序列號(Serial Number)屬性
Function Get-SPInventory {
PROCESS {
$wmi = Get-WmiObject Win32_OperatingSystem -comp $_ | Select CSName,BuildNumber,ServicePackMajorVersion
$obj = New-Object PSObject
$obj | Add-Member NoteProperty BuildNumber ($wmi.BuildNumber)
$obj | Add-Member NoteProperty CSName ($wmi.CSName)
$obj | Add-Member NoteProperty SPVersion ($wmi.ServicePackMajorVersion)
$wmi = Get-WmiObject Win32_BIOS -comp $_ | Select SerialNumber
$obj | Add-Member NoteProperty BIOSSerial ($wmi.SerialNumber)
Write-Output $obj
}
}


更靈活的輸出

爲了對自定義對象結果用於輸出,我爲這個函數的創建了各種可能的用途,比如,只顯示Windows 2008服務器的電腦,可以使用
Gc c:\computernames.txt | Get-SPInventory | Where { $_.BuildNumber -eq 6001 }

或者,使用HTML文件來顯示所有沒有安裝SP1的Windows Vista電腦,可以使用
Gc c:\computernames.txt | Get-SPInventory | Where { $_.BuildNumber -eq 6000 } | ConvertTo-HTML | Out-File c:\VistaInventory.html

其他的用法是相當多的 -XML(輸出到XML格式), CSV(輸出到XML格式), Table(輸出到表格形式), Lists(以清單格式顯示), HTML(以HTML格式輸出), Sorted(排序), Filtered(過濾), Grouped(分組) 等等。 內置的Windows PowerShell的Cmdlets就可以實現,並不需要其他另外的工作。


更靈活的輸入

不幸的是,我的輸入不是很靈活,這是Windows PowerShell版本1中的弱點,而在版本2中引入了腳本Cmdlets, 這就提供了相當多的靈活性。
我的函數希望輸入是簡單的字符串對象,比如,如果我使用cmdlet來取代get-content命令的話,該功能不能工作 (Get-Content是一個命令,可以從活動目錄中拿到計算機名),就如
Get-QADComputer | Get-SPInventory

在這裏,get-QADComputer命令返回的對象是包含名稱屬性,而不是返回簡單的字符串對象。爲了讓這個命令能夠運行,我需要去修改這個函數,請注意圖片4。請注意其中的變化(用紅色字體顯示的)
(是免費工具活動目錄管理Cmdlets中的一部分,你可以從quest.com/powershell中得到)
clip_p_w_picpath001[2] 圖4 最終結果
Function Get-SPInventory {
    PROCESS {
        $wmi = Get-WmiObject Win32_OperatingSystem –comp $_.Name | Select CSName,BuildNumber, ServicePackMajorVersion
        $obj = New-Object PSObject
        $obj | Add-Member NoteProperty BuildNumber ($wmi.BuildNumber)
        $obj | Add-Member NoteProperty CSName ($wmi.CSName)
        $obj | Add-Member NoteProperty SPVersion ($wmi.ServicePackMajorVersion)
        $wmi = Get-WmiObject Win32_BIOS –comp $_.Name | Select SerialNumber
        $obj | Add-Member NoteProperty BIOSSerial ($wmi.SerialNumber)
        Write-Output $obj
    }
}

我的函數是作用在管道對象的名稱屬性上的,而不是把管道所有對象都傳遞給參數-computername。,就靈活性而言,這個小改變很有意義---通過get-QADComputer函數,我可以把我的輸入限定到一個特定的組織單元,比如只有符合活動目錄某屬性的電腦


有錯誤,怎麼辦?

可以肯定的是,這個函數肯定會遇到某些無法連接的電腦,或者沒有權限去連接的電腦,我們的函數,到現在爲止,可以在Windows PowerShell界面中以紅色字體顯示錯誤信息,然後繼續去找下一臺電腦

當然,這可能也就是你想要的。但是你也有可能不想顯示這些錯誤信息,我們也可以通過在函數的Process 腳本塊中添加下面一行
$ErrorActionPreference = "SilentlyContinue"
然後,你可能需要一個更復雜的方法來創建錯誤信息日誌,可以顯示那些不能完成的計算機名稱。 Windows PowerShell可以做到這點,但是你要等到下個月才能夠看到如何做到(在這裏,你就要等到下個禮拜才能看到)


讓他變得真正有用起來

現在在,可能你已經把這個函數輸入到一個.PS1文件中然後再運行這個腳本。這樣已經很好了,但是還是有些可用性方面的問題,有一個問題是,無論你什麼時候想用這個函數,你需要打開這個腳本問題,修改啓用這個函數的命令行(添加輸出,排序或者其他的命令),保存腳本,然後再運行。
如果能夠像Cmdlet那樣,能夠直接在Windows Powershell中直接調用該函數
這裏有兩種辦法,
第一種就是簡單的把函數複製到Windows Powershell屬性腳本中,該腳本是Poweshell每次啓動時會自動運行的四個腳本文件之一(如果存在的話),在Powershell的快速入門指南中列出了這四個位置。
我一般使用的是名爲Profile.PS1的文件,它必須位於“我的文檔”文件夾中的Windows-PowerShell文件夾中,一旦函數在你的配置文件後,你可以直接在Shell命令提示符直接使用
第二種辦法就是,將函數(只有函數,不含任何其他代碼)包含在腳本中,然後將該腳本以“點源”(dot source) 方式置入 shell 中。當運行腳本時,Windows PowerShell 通常都會爲該腳本創建新作用域。該腳本內發生的任何操作都會在該作用域內進行,例如函數的定義。當腳本結束運行時,該作用域會被棄用,發生在該作用域內的一切操作隨之丟失。
因此,請記住,如果你擁有隻包含 Get-SP Inventory 函數的腳本文件,那麼運行該腳本將會創建新作用域、定義該函數,然後棄用該作用域,該函數會隨之消失。顯然,這不太實用。
相反,通過 dot source 運行腳本不會創建新作用域。如果我把 Get-SPInventory 置於名爲 MyFunction.ps1 的文件中,我可以以如下所示方式 dot source 它:
PS C:\> . c:\functions\myfunction

請注意腳本的路徑和文件名前面的單個句點後面有個空格。這會告知 shell 在當前作用域中運行腳本,這意味着發生在腳本中的一切操作在腳本結束運行後會保存下來。結果 Get-SPInventory 腳本現在已定義在 shell 中,而您現在可以直接從命令行使用它,幾乎像使用 cmdlet 一樣。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章