英文,Scripting Guy ! 建立 A Self-Documenting 的指令碼
The Microsoft Scripting Guys
若要描述的廣泛 brushed,透過在布宜諾斯艾利斯下天空的藍色 incredible 深度,您必須使用特殊的色彩名稱。 鈷藍色、 deep-sea 藍色午夜藍色、 神聖-mackerel 藍色 — 這些甚至沒有開始 portray 實際出現在座艙罩覆蓋舊的城市的色彩。 對齊的世紀-舊的建築物的寬度 sidewalks 是點與小的資料表。 我坐在空的金屬椅子。 perfumed 的快速暫緩 drifts,藉由執行 cinnamon、 cardamom 和 thyme 的提示。 與 wings outstretched seagulls 瀏覽的 updrafts engender envy 大的 Kahuna 自己的足夠技能、 寬限期,和 poise。 在 café con leche 似乎特別 apropos,強式,以要求自己的注意,針對從 sights 和聲音 downtown 布宜諾斯艾利斯的競爭性。 我在鎮,使用 [VBScript] 來協助管理其網路討論 Microsoft Premier 客戶。 我從 [我的網路攝影機包中擷取摺疊的對應,並繼續 Nueve de Julio Avenue 計劃從 Calle 佛羅里達州到 [Obelisk 我路由。
我發現我在地圖上的位置。 我是購物中心,Galerias Pacifico 附近。 我開啟對應上好幾次我嘗試在時間和空間熟悉與我的位置。 這個時間,會出現高、 辨別-尋找男人 three-piece 的黑暗花色,並代表只要超出在日光。 他的陰影,地圖上, playfully 舞可讓我瀏覽設定。 我可以獨立,他說,"Buenos dias,se˜nor 」。
「 Buenos dias"盡責回覆。
「 喔,您是為美國可能? 」 他回覆與稍微英的強調文字。
我是有點 aback,並在 affirmative hesitantly 回覆。
他 inquires 「 是這您第一次在我們相當的城市? 」。
再次,我正在 reticent 但說 [是]。
「 然後我可能會提供我鄉下服務給您。 是,您也可以搜尋? 」 他 politely 要求。
我知道他,他給我的說明。 之後我們聊天室的幾分鐘的時間。
哇 ! 」 我想以自己。 " 自我記錄的城市 ! 不是需要在此對應。 人是易懂您難以會有來查看地圖,有人會有協助您之前的時間 」。
這是實際上是幾年前。 現在,在北卡羅來那州我家外, 天氣是可愛提醒我布宜諾斯艾利斯我瀏覽的 — 和自我記錄的功能可以是如何不錯。 如果我們花時間來計劃,繼續進行,我們就可以有執行類似的服務,讓我們,我們指令碼。 在這的篇文章我們將會看到開發指令碼,將會收集某些模式的其他指令碼註解。 我們可以修改的模式以符合自己需求的並且可以展開技術之外建立的指令碼文件。
讓我們來看看指令碼,已仔細放在某些文件。 在 [圖 1 (這專為在 「 嗨 Scripting Guy ! GetSetIEStartPage.ps1 指令碼中 文件 」 我要如何變更我的 Internet Explorer 首頁?「,放在這裡字串然後指派給變數的名稱的 $ 註解的註解。 在這裡的字串為開頭標記的 delineated 的符號和引號 (@ 」) 和結尾標記的引號和一個 at 」 (@) 符號。 有三個註解區塊,我們有興趣就可以取得。 第一個包含一般的指令碼標頭資訊: 標題、 作者、 日期、 關鍵字和指令碼本身上的註解。 第二個和第三個註解區塊特別與相關指令碼中包含兩個主要函式。
[圖 1 GetSetIEStartPage.ps1
Param([switch]$get,[switch]$set,$computer="localhost")
$Comment = @"
NAME: GetSetIEStartPage.ps1
AUTHOR: ed wilson, Microsoft
DATE: 1/5/2009
KEYWORDS: stdregprov, ie, [wmiclass] type accelerator,
Hey Scripting Guy
COMMENTS: This script uses the wmiclass type accelerator
and the stdregprov to get the ie start pages and to set the
ie start pages. Using ie 7 or better you can have multiple
start pages
"@ #end comment
Function Get-ieStartPage()
{
$Comment = @"
FUNCTION: Get-ieStartPage
Is used to retrieve the current settings for Internet Explorer 7 and greater.
The value of $hkcu is set to a constant value from the SDK that points
to the Hkey_Current_User. Two methods are used to read
from the registry because the start page is single valued and
the second start pages key is multi-valued.
"@ #end comment
$hkcu = 2147483649
$key = "Software\Microsoft\Internet Explorer\Main"
$property = "Start Page"
$property2 = "Secondary Start Pages"
$wmi = [wmiclass]"\\$computer\root\default:stdRegProv"
($wmi.GetStringValue($hkcu,$key,$property)).sValue
($wmi.GetMultiStringValue($hkcu,$key, $property2)).sValue
} #end Get-ieStartPage
Function Set-ieStartPage()
{
$Comment = @"
FUNCTION: Set-ieStartPage
Allows you to configure one or more home pages for IE 7 and greater.
The $aryValues and the $Value variables hold the various home pages.
Specify the complete URL ex: "http://www.ScriptingGuys.Com" make sure
to include the quotation marks around each URL.
"@ #end comment
$hkcu = 2147483649
$key = "Software\Microsoft\Internet Explorer\Main"
$property = "Start Page"
$property2 = "Secondary Start Pages"
$value = "https://www.microsoft.com/technet/scriptcenter/default.mspx"
$aryValues = "https://social.technet.microsoft.com/Forums/en/ITCG/threads/",
"https://www.microsoft.com/technet/scriptcenter/resources/qanda/all.mspx"
$wmi = [wmiclass]"\\$computer\root\default:stdRegProv"
$rtn = $wmi.SetStringValue($hkcu,$key,$property,$value)
$rtn2 = $wmi.SetMultiStringValue($hkcu,$key,$property2,$aryValues)
"Setting $property returned $($rtn.returnvalue)"
"Setting $property2 returned $($rtn2.returnvalue)"
} #end Set-ieStartPage
# *** entry point to script
if($get) {Get-ieStartpage}
if($set){Set-ieStartPage}
從原始程式檔中寫入另一個文件註解,我們需要開啟,原始的指令碼的註解中搜尋,然後撰寫適當的文字到新的檔案。 聽起來簡單。 喔,嗯,我們也需要新的指令碼的名稱。 讓我們來呼叫它 GetCommentsFromScript.ps1。
[圖 2 ,所示 GetCommentsFromScript.ps1 指令碼開頭一個 Param 陳述式用來允許我們提供給指令碼在執行階段的資訊而定。 以下是參數陳述式。
[圖 2 GetCommentsFromScript.ps1
Param($Script= $(throw "The path to a script is required."))
Function Get-FileName($Script)
{
$OutPutPath = [io.path]::GetTempPath()
Join-Path -path $OutPutPath -child "$(Split-Path $script-leaf).txt"
} #end Get-FileName
Function Remove-OutPutFile($OutPutFile)
{
if(Test-Path -path $OutPutFile) { Remove- Item $OutPutFile | Out-Null }
} #end Remove-OutPutFile
Function Get-Comments($Script,$OutPutFile)
{
Get-Content -path $Script |
Foreach-Object `
{
If($_ -match '^\$comment\s?=\s?@"')
{
$beginComment = $True
} #end if match @"
If($_ -match '"@')
{
$beginComment = $False
} #end if match "@
If($beginComment -AND $_ -notmatch '@"')
{
$_ | Out-File -FilePath $OutPutFile -append
} # end if beginComment
} #end Foreach
} #end Get-Comments
Function Get-OutPutFile($OutPutFile)
{
Notepad $OutPutFile
} #end Get-OutPutFile
# *** Entry point to script ***
$OutPutFile = Get-FileName($script)
Remove-OutPutFile($OutPutFile)
Get-Comments -script $script -outputfile $OutPutFile
Get-OutPutFile($OutPutFile)vw
Param($Script= $(throw "The path to a script is required."))
使用命令列參數的優點是我們不需要開啟指令碼,並編輯它提供指令碼的註解,我們要複製的路徑中。 我們正在這個參數強制指派預設值給變數 $ 指令碼。 預設值使用 throw 指令會引發錯誤,並這表示指令碼將會永遠引發執行時發生錯誤,除非我們提供 –script 參數的值。
讓我們 digress 一分鐘,並看看如何在 [圖 3 ] 中的 DemoThrow.ps1 指令碼中使用 throw 陳述式。 若要取得過去由 Set-錯誤函式中 throw 陳述式所引發的錯誤,我們首先需要將 $ errorActionPreference 變數設定為 SilentlyContinue。 這可以防止錯誤所顯示,並可讓指令碼繼續執行。 它是從 VBScript On Error Resume Next] 設定相同的。 如果陳述式用來評估 $ 值的變數。 如果有符合的項目發生 throw 陳述式,並回的例外狀況。 要評估錯誤,我們使用 Get-ErrorDetails 函式。 發生,第一件事會是的錯誤計數的會具有遞增 1 因為 throw 陳述式所引發錯誤的顯示。 然後我們採取的第一個錯誤 (錯誤的索引值為 0 是永遠最新),並將錯誤物件傳送至 Format-List cmdlet。 我們選擇的所有屬性。 不過,在引動過程的資訊將傳回為物件,而且因此,我們需要直接查詢的物件。 我們這麼存取透過錯誤物件的 InvocationInfo 屬性引動過程物件。 產生的錯誤資訊如 [圖 4 ] 所示。
[圖 3 DemoThrow.ps1
Function Set-Error
{
$errorActionPreference = "SilentlyContinue"
"Before the throw statement: $($error.count) errors"
$value = "bad"
If ($value -eq "bad")
{ throw "The value is bad" }
} #end Set-Error
Function Get-ErrorDetails
{
"After the throw statement: $($error.count) errors"
"Error details:"
$error[0] | Format-List -Property *
"Invocation information:"
$error[0].InvocationInfo
} #end Get-ErrorDetails
# *** Entry Point to Script
Set-Error
Get-ErrorDetails
[圖 4 : Throw 陳述式用來引發錯誤
現在讓我們先回到我們主要的指令碼 GetCommentsFromScript.ps1。 我們需要一個函式會建立新文字文件會包含所有的註解 gleaned 從指令碼的檔案名稱。 若要這麼做,我們可以使用函式的關鍵字,並隨後它函式的名稱。 我們將會呼叫我們函式取得-檔案名稱,Windows PowerShell 動詞-名詞 」 命名慣例的一致。 Get-FileName 需要一個單一輸入的參數將會保留在函式內,$ 指令碼變數的指令碼以進行分析路徑。 以下是 Get-FileName 函式之項目:
Function Get-FileName($Script)
{
接下來我們必須取得至本機電腦上暫存資料夾路徑。 種許多要執行這項操作,包括使用環境的 Windows PowerShell 的磁碟機。 不過,我們決定使用靜態 GetTempPath 方法,從 io.path 的.NET Framework 類別。 在的 GetTempPath 方法會傳回在的路徑,我們將在其中儲存新建立的文字檔為暫存資料夾。 我們會保留暫存資料夾路徑在 $ OutPutPath 變數,如下所示:
$OutPutPath = [io.path]::GetTempPath()
我們決定我們新的文字檔案命名為指令碼的名稱之後。 要執行這項操作,我們要分開指令碼儲存在路徑上的指令碼名稱。 我們可以使用分隔的路徑 Cmdlet 來執行這個向。 –leaf 參數會指示 Cmdlet,傳回指令碼名稱。 如果我們有希望的目錄路徑,包含指令碼,我們會使用 –parent 參數。 由於我們希望先,發生該作業,然後我們會將錢幣符號,可執行程式碼,並傳回指令碼的名稱來建立一個 Sub-expression 括號前面的我們會將一組括號內分割路徑命令。 我們為我們的文字檔案,可以 use.ps1 做為副檔名,但是,可能是令人混淆因為它是一個指令碼的延伸。 因此,我們只會將傳回的檔名的副檔名.txt,並將整個圖表一對引號。 現在,我們使用聯結的路徑 Cmdlet,我們的輸出檔案中建立新的路徑。 新的路徑是組成暫存資料夾儲存在 $ OutPutPath 變數和檔案名稱,我們建立使用分隔的路徑。 我們無法使用字串操作和串連建立新的檔案路徑,但並更更可靠,使用加入的路徑] 和 [分隔的路徑來執行這些類型的作業。 以下的程式碼的樣子:
Join-Path -path $OutPutPath -child "$(Split-Path $script-leaf).txt"
} #end Get-FileName
我們現在需要決定我們要如何處理重複的檔案。 我們無法提示使用者藉由說會有一個重複的檔案,使用像這樣的程式碼:
$Response = Read-Host -Prompt "$OutPutFile already exists. Do you wish to delete it <y / n>?"
if($Response -eq "y")
{ Remove-Item $OutPutFile | Out-Null }
ELSE { "Exiting now." ; exit }
我們無法實作某些命名的演算法,可讓現有的檔案的備份,以.old 副檔名將它重新命名。 如果我們做了這,程式碼看起來可能如下:
if(Test-Path -path "$OutPutFile.old") { Remove-Item-Path "$OutPutFile.old" }
Rename-Item -path $OutPutFile -newname "$(Split-Path $OutPutFile -leaf).old"
或只是我們無法刪除現有檔案是我的選擇。 我們要執行的動作將皆您,我們藉由使用函式的關鍵字,並指定名稱的函式所建立,Remove-outputfile 函式中的位置。 我們要使用 $ outputfile,提供輸入該的函式,如下所示:
Function Remove-OutPutFile($OutPutFile)
{
若要判斷如果檔案存在時中,,我們使用測試路徑 Cmdlet,並提供路徑參數 $ outputfile 變數中所包含的字串。 測試路徑 Cmdlet 會傳回只 true 」 或 「 一項 False 視是否找到檔案。 這表示我們可以使用,如果陳述式來評估該檔案是否存在。 如果找到,檔案,我們在指令碼區塊中執行動作。 如果不找檔案,會無法執行指令碼區塊。 您可以查看此處的第一個命令會找不到的檔案,並傳回 False。 在第二個的命令在因為無法找到檔案無法執行指令碼區塊:
PS C:\> Test-Path c:\missingfile.txt
False
PS C:\> if(Test-Path c:\missingfile.txt){"found file"}
PS C:\>
Remove-outputfile,函式內,如果陳述式會用來判斷是否已參考 $ outputfile 檔案存在。 如果存在,它就會刪除使用 Remove-項目指令程式。 在刪除檔案時,通常傳回的資訊以管線,out-null Cmdlet,提供無訊息的作業。 程式碼如下所示:
if(Test-Path -path $OutPutFile) { Remove-Item $OutPutFile | Out-Null }
} #end Remove-OutPutFile
我們也已經建立在輸出檔案的名稱,並刪除任何先前的輸出檔案,可能會產生周圍之後,都會擷取註解,從指令碼的時間。 要執行這項操作,我們建立 Get-註解函式並將它傳遞 $ 指令碼變數和 $ outputfile 變數,如下所示:
Function Get-Comments($Script,$OutPutFile)
{
現在我們要讀取指令碼使用 Get-Content] Cmdlet 中,我們傳遞路徑至指令碼的文字。 當我們使用 Get-Content 讀取檔案檔案一次讀取一行,並每一行傳遞以及管線。 如果我們將結果儲存在變數,我們會有陣列。 我們可以將變數 $ 一個像任何其他陣列,包括取得透過長度屬性陣列中的元素數目,以及直接到如下所示的陣列索引:
PS C:\fso> $a = Get-Content -Path C:\fso\ GetSetieStartPage.ps1
PS C:\fso> $a.Length
62
PS C:\fso> $a[32]
($wmi.GetMultiStringValue($hkcu,$key, $property2)).sValue
以下是讀取輸入的指令碼,並傳送以及管線的那一行:
Get-Content -path $Script |
接下來,我們需要在每一行,以查看它是否屬於註解區塊中尋找。 若要檢查在管線內的每一行,我們會使用 ForEach-Object Cmdlet,是類似 ForEach…Next 的陳述式,它可讓我們使用中的個別物件,一次一個在集合中。 回復的刻度字元 (') 用來繼續執行下一行命令。 我們要是來自透過管線,每個物件上所執行的動作被包含在一個指令碼區塊 delineated 具有一組 (也稱為大括弧) 的大括號中。 Get-Content 函式的這個部分會出現在這裡:
ForEach-Object `
{
當我們在 ForEach-Object Cmdlet 的處理序區塊內時,我們要檢查的文字行。 若要這麼做,我們要使用,如果陳述式。 $ _ 自動變數用來表示目前行的管線。 我們可以使用 –match 運算子來執行規則運算式模式符合對文字的行。 –match 運算子會在 –match 運算子的右邊布林值 True 或 False 傳回以回應模式的開始的模式。 此部份指令碼如下所示:
PS C:\fso> '$Comment = @"' -match '^\$comment\s?=\s?@"'
True
我們使用規則運算式模式是由特殊的字元數所組成:
^ 在開始 —Match
\ —Escape 字元,所以 $ 符號被視為常值字元 」 和 「 不規則運算式中使用的特殊字元
$ 註解 —Literal 字元
\ s? —Zero 一或多個泛空白字元 (White Space) 字元
= —Literal 字元
\ s? —Zero 一或多個泛空白字元 (White Space) 字元
@ 」,常值 (Literal) 字元
檢查目前的管線上的文字行的程式碼區段顯示如下:
If($_ -match '^\$comment\s?=\s?@"')
我們建立一個變數的名稱的 $ beginComment 用來標記註解區塊的開頭。 如果我們做過 –match 陳述式,我們發現註解區塊的開頭。 我們設定變數的等於為 $ true 如這裡見:
{
$beginComment = $True
} #end if match @"
接下來我們檢查看我們是否註解區塊的結尾。 要執行這項操作,我們再次使用 –match 運算子。 我們的這一次,「 @ 字元順序,用來關閉在這裡的字串。 如果我們找到,我們會將 $ beginComment 變數設定為 False 中:
If($_ -match '"@')
{
$beginComment = $False
} #end if match "@
我們做它過去的前兩個如果陳述式: 第一個會識別這裡-字串的開頭,並將第二個會找出在這裡-字串的結尾。 現在我們要抓取,要寫入至我們的回應檔案的文字。 要執行這項操作,我們希望 $ beginComment 變數設定為 true。 我們也要確定我們沒有看到,在登入引號 (@ 」) 字元列上,因為它是表示,這裡的字串的結尾。 為了讓這個判斷,我們會使用複合的如果陳述式:
If($beginComment -AND $_ -notmatch '@"')
{
現在我們寫入文字輸出檔案。 要執行這項操作,我們會使用 $ _ 自動變數,表示目前的文字行 ; 我們管線它在 out-file Cmdlet。 在 out-file Cmdlet 會接收包含註解的檔案路徑,$ outputfile 變數。 我們可以使用 –append 參數來指定我們要從回應檔案中的指令碼中收集的所有註解。 如果我們沒有使用附加參數,文字檔會包含最後一個註解,因為,預設的情況下,out-file Cmdlet 會覆寫其內容。 我們再關閉所有括弧。 我會考慮它將註解加入每個右大括號,表示在大括弧的用途之後最佳的作法。 這對指令碼更容易讀取,以及容易疑難排解並維護:
$_ | Out-File -FilePath $OutPutFile -append
} # end if beginComment
} #end ForEach
} #end Get-Comments
我們現在會建立函式呼叫 Get-outputfile,將會開啟輸出檔的讀取。 因為不容易找到的暫存資料夾],而且我們必須 $ outputfile 變數中的檔案路徑,它使用合理指令碼開啟輸出檔案。 Get-outputfile 函式會接收一個單一輸入變數呼叫的 $ outputfile,其中包含我們要開啟,回應檔案路徑。 當我們呼叫 Get-outputfile 函式時,我們會將 $ outputfile 傳遞給它。 我們無法傳遞我們要的任何值,Get-outputfile 函式,] 和 [函式的值會是 「 $ outputfile 變數參考內。 我們甚至可以將字串直接 (而不使用引號括住字串) 傳遞至函式:
Function Get-OutPutFile($OutPutFile)
{
Notepad $OutPutFile
} #end Get-OutPutFile
Get-OutPutFile -outputfile C:\fso\GetSetieStartPage.ps1
一般而言,如果您要收集要傳遞至函式的項目,請撰寫一個的指令碼時, 是個好主意 encase 相同的變數名稱都是內部和外部函式將使用中的資料。 這會遵循我們的指令碼開發的最佳作法之一: 「 不影響與指令碼的工作者區段 」。 在這個範例中,當我們呼叫函式我們的 」 執行工作 」。 這在未來變更指令碼會需要我們編輯的字串常值 (Literal) 值。 藉由在變數中放置字串,我們可以輕鬆編輯,變數的值。 我們是,設定實際上,提供經由命令列或透過項目在另一個函式中完成,變數的值。 儘可能,您應該避免字串常值直接放在指令碼中。 在遵循碼我們使用變數保留會傳遞至 Get-outputfile 函式之檔案的路徑:
Function Get-OutPutFile($OutPutFile)
{
Notepad $OutPutFile
} #end Get-OutPutFile
$OutPutFile = "C:\fso\GetSetieStartPage.ps1"
Get-OutPutFile -outputfile $OutPutFile
完整的 Get-outputfile 函式如下所示:
Function Get-OutPutFile($OutPutFile)
{
Notepad $OutPutFile
} #end Get-OutPutFile
不需要輸入字串常值的輸出檔路徑中的 $ outputfile 變數會接收由 Get-FileName 函式所建立的路徑。 Get-FileName 函式會接收包含要擷取的註解指令碼路徑。 透過命令列參數有這個指令碼路徑。 函式會有單一的輸入的參數時, 您可以傳遞它給函式使用一組括號。 如果,從另一方面來說,函式會使用兩個或多個輸入參數,您必須使用 –parameter 名稱語法:
$OutPutFile = Get-FileName($script)
接下來,我們呼叫 Remove-outputfile 函式 (先前討論過),並將它路徑傳遞給 [outputfile $ outputfile 變數中所包含:
Remove-OutPutFile($OutPutFile)
當我們確保我們的輸出檔的名稱時,我們就會呼叫 Get-註解函式以擷取 $ 指令碼變數所表示的路徑,指令碼的註解。 註解將會寫入 $ outputfile 變數所參考的輸出檔。 這行程式碼會出現在這裡:
Get-Comments -script $script -outputfile $OutPutFile
當註解所所有已撰寫到輸出檔案時,我們最後呼叫 Get-outputfile 函式並將 $ outputfile 變數中所包含的路徑傳遞給它。 如果您不希望被開啟回應檔案,您就可以輕鬆地將該行註解超出您的指令碼,或直接從您的指令碼中刪除它和 Get-outputfile 函式本身。 如果您有興趣只能儲存它之前,先檢閱每個檔案的保留位置的程式碼行:
Get-OutPutFile($OutPutFile)
當 GetCommentsFromScript.ps1 指令碼執行時,則沒有的確認訊息會顯示在螢幕上中。 [圖 5 ] 中如下所示,在記事本中, 所顯示的新建立的文字檔的存在只確認的指令碼。
[圖 5 顯示在 [記事本] 中: 新的文字檔
GetCommentsFromScript.ps1 指令碼可以輕易地適用您自己的方式撰寫指令碼的或甚至從文字為主的記錄檔中收集其他種類的文件。 您只需要修改規則運算式模式,用於標記開頭,您有興趣收集部分文字的結尾。 我們希望您喜歡此的指令碼,並邀請您加入我們,指令碼中心,我們將發佈 Scripting Guy 在新 「 嗨上 ! 文件的每個工作日。
Ed Wilson ,一個已知的指令碼專業人員會是八個包括 Windows PowerShell 指令碼指南 》 (Microsoft Press 2008) 和 Microsoft Windows PowerShell 的 Step,by Step (Microsoft Press 2007) 的書籍的作者。 Ed 會保留 20 個以上的產業認證包括 Microsoft Certified Systems Engineer (MCSE) 以及認證資訊系統安全性專業 (CISSP)。 在他備用的時間,他樂於 woodworking underwater 相片,、 scuba 投擲。 並 tea。
Craig Liebendorfer 是 wordsmith 和 longtime Microsoft Web 的編輯器)。 Craig 仍然無法相信還有這麼他使用字每一天的工作。 其中一個他喜愛的事物是 irreverent 幽默的因此他應該符合在這裡的權限。 他會認為要他 magnificent 女兒生活中的他最大叫好。