英文,Scripting Guy ! 使用 Windows PowerShell 中建立 CAB 檔案

The Microsoft Scripting Guys

內容

展開封包檔
使用 makecab.exe 公用程式

的其中一個我 是有關在壞人者在資料 smuggler 在未來的最喜愛的電影 (「 助憶 Johnny 鍵 」)。 他會到各種不同的國家 / 地區,並與用戶端。 他在插座 surgically implanted 到他的標頭的基底,並且客戶會插入在插座和傳輸大量的資料到他的大腦 Zune-尋找裝置。 因為這個資料 smuggler 他的大腦分割 evidently 使用 NTFS 檔案使用權限,他沒有權限,他的大腦走私而中毒的資料部分。 資料已因此安全 — 即使是從 smuggler 的自己旅行的想法。 其中的場景的用戶端希望他 Courier 資料,在大量量,但 evidently 我們 smuggler 沒有夠大的容量,來執行許多的資料。 如此做他做什麼? 他壓縮他的大腦 — 我喜歡!

資料壓縮只電影的好 fodder,無法它同樣適用於網路系統管理員。 我們都知道,最可能使用,不同檔案壓縮公用程式。 我會使用它們每星期,我在 Microsoft Press 的編輯器,透過電子郵件傳送新章節的 [我的活頁簿。 我執行這項操作不會有太多因為我是頻寬限制,但因為我們有電子郵件的配額限制,我們可以儲存電子郵件的大小,傳送,接收。 我需要封存檔案從 [我的膝上型電腦的其中一個浮動的 [我的 Office 解決數個攜帶型磁碟時,我還擁有使用檔案壓縮公用程式。

當我用來旅行世界教導研討會的指令碼時,我會被要求定期,「 如何可以我壓縮檔案透過指令碼? 」 和我會回答,「 您需要購買一個協力廠商公用程式支援命令列參數 」。 有一天,我已閱讀的登錄,然後和我執行在名為 (有趣) 的 COM 物件 Makecab.Makecab。 嗯,哪些您認為該物件會? 沒錯,您說的沒錯。 它可讓您使封包 (.cab) 檔案 — 高度壓縮由封裝和部署的各種不同的應用程式的檔案。 但沒有任何項目要停止的 enterprising 的網路系統管理員] 或 [從佔用這些工具本身的指令碼專家。 而且,只是我們會做。 讓我們來開頭, [圖 1 ] 中的指令碼。

[圖 1 CreateCab.ps1

Param(
  $filepath = "C:\fso", 
  $path = "C:\fso\aCab.cab",
  [switch]$debug
  )
Function New-Cab($path,$files)
{
 $makecab = "makecab.makecab"
 Write-Debug "Creating Cab path is: $path"
 $cab = New-Object -ComObject $makecab
 if(!$?) { $(Throw "unable to create $makecab object")}
 $cab.CreateCab($path,$false,$false,$false)
 ForEach ($file in $files)
 {
 $file = $file.fullname.tostring()
 $fileName = Split-Path -path $file -leaf
 Write-Debug "Adding from $file"
 Write-Debug "File name is $fileName"
 $cab.AddFile($file,$filename)
 }
 Write-Debug "Closing cab $path"
 $cab.CloseCab()
} #end New-Cab

# *** entry point to script ***
if($debug) {$DebugPreference = "continue"}
$files = Get-ChildItem -path $filePath | Where-Object { !$_.psiscontainer }
New-Cab -path $path -files $files

CreateCab.ps1 –filepath C:\fso1

在我們要做的第一件事是建立使用參數的陳述式的某些命令列參數。 參數的陳述式必須是在第一個的 noncommented 的一行,指令碼中。 當指令碼從 Windows PowerShell 主控台內執行或是從在一個指令碼編輯器 」 中,命令列參數用來控制指令碼的執行的方式。 這的種方式,您不必編輯指令碼在的每次您要建立.cab 檔案從在不同的目錄。 您必須只提供 –filepath 參數,新值,如下所示:

有關命令列參數,好的方法是它們就會使用這表示您需要提供的部分參數完成唯一參數是唯一。 因此,您可以使用在命令列語法如下:

CreateCab.ps1 –f c:\fso1 –p c:\fso2\bcab.cab –d

這個語法會搜尋 c:\fso1 目錄,並取得所有檔案中。 然後,它會建立名為 bcab.cab fso2 c 的磁碟機資料夾中的封包檔案。 執行時,它也會產生偵錯資訊。 請注意 –debug 參數是表示它會影響指令碼只時出現一個切換的參數。 以下是 CreateCab.ps1 指令碼的相關的區段:

Param(
  $filepath = "C:\fso", 
  $path = "C:\fso\aCab.cab",
  [switch]$debug
  )

現在我們將建立新的封包的函式,它接受兩個輸入的參數、 –path 和 –files:

Function New-Cab($path,$files)

您可以指定在程式識別碼 Makecab.Makecab 若要在變數名稱 $ Makecab,這會讓指令碼位元更容易閱讀。 這是也在合適的地方,將第一個寫入偵錯陳述式:

{
 $makecab = "makecab.makecab"
 Write-Debug "Creating Cab path is: $path"

接下來我們將建立 COM 物件:

 $cab = New-Object -ComObject $makecab

可以使用 $ 來完成的順序是一些錯誤檢查? 自動變數:

 if(!$?) { $(Throw "unable to create $makecab object")}

嘗試建立 Makecab.Makecab 物件時沒有錯誤發生則您可以使用 $ 封包的變數中所包含的物件,並呼叫 CreateCab 方法:

 $cab.CreateCab($path,$false,$false,$false)

建立.cab 檔案之後您可以加入檔案它使用 ForEach 陳述式:

 ForEach ($file in $files)
 {
 $file = $file.fullname.tostring()
 $fileName = Split-Path -path $file -leaf

您已先變成字串的完整檔案名稱,並移除使用分隔的路徑 Cmdlet 的目錄資訊之後,您會包含另一個寫入偵錯陳述式,讓指令碼的使用者知道什麼是在這種方式:

 Write-Debug "Adding from $file"
 Write-Debug "File name is $fileName"

接下來您將檔案加入至封包檔時:

 $cab.AddFile($file,$filename)
 }
 Write-Debug "Closing cab $path"

若要關閉封包檔案,您可以使用 CloseCab 方法:

 $cab.CloseCab()
} #end New-Cab

現在我們將會移至指令碼的進入點。 首先我們檢查看是否正在執行指令碼在偵錯模式中尋找的 $ 偵錯變數。 如果偵錯 $ 變數不存在,您不必執行任何動作。 如果它有您需要設定 $ DebugPreference 變數来繼續,可列印在螢幕上所要寫入偵錯陳述的值。 預設的情況下,在 $ DebugPreference 設定為的方法不執行任何略過超過命令的 silentlycontinue。 程式碼如下:

if($debug) {$DebugPreference = "continue"}

現在您需要取得檔案的集合。 要執行這項操作,您可以使用 Get-ChildItem Cmdlet:

$files = Get-ChildItem -path $filePath | 
 Where-Object { !$_.psiscontainer }

然後您傳遞集合至函新封包式如下所示:

New-Cab -path $path -files $files

當您在偵錯模式中執行 CreateCab.ps1 指令碼時, 您將看到輸出中顯示 [圖 2] .

fig02.gif

[圖 2 執行 CreateCab.ps1 在偵錯模式中

展開封包檔

您不能使用 Makecab.Makecab 物件以展開封包檔,因為它沒有一個展開的方法。 也可以您使用 Makecab.expandcab 物件,因為它不存在。 但能夠展開封包的檔案是 Windows Shell 中, 固有的因此您可以使用 Shell 物件。 若要存取殼層,您可以使用 Shell.application COM 物件,ExpandCab.ps1 指令碼,在 [圖 3 ] 所示。

[圖 3 ExpandCab.ps1

Param(
  $cab = "C:\fso\acab.cab",
  $destination = "C:\fso1",
  [switch]$debug
  )
Function ConvertFrom-Cab($cab,$destination)
{
 $comObject = "Shell.Application"
 Write-Debug "Creating $comObject"
 $shell = New-Object -Comobject $comObject
 if(!$?) { $(Throw "unable to create $comObject object")}
 Write-Debug "Creating source cab object for $cab"
 $sourceCab = $shell.Namespace($cab).items()
 Write-Debug "Creating destination folder object for $destination"
 $DestinationFolder = $shell.Namespace($destination)
 Write-Debug "Expanding $cab to $destination"
 $DestinationFolder.CopyHere($sourceCab)
}

# *** entry point ***
if($debug) { $debugPreference = "continue" }
ConvertFrom-Cab -cab $cab -destination $destination

第一次指令碼建立在命令列的參數,就像 CreateCab.ps1:

Param(
  $cab = "C:\fso\acab.cab",
  $destination = "C:\fso1",
  [switch]$debug
  )

會接下來它建立函,ConvertFrom-封包式,它會接受兩個命令列參數,一個包含.cab 檔案,另一個,包含以展開檔案目的端:

Function ConvertFrom-Cab($cab,$destination)

現在您還會建立在 Shell.Application 物件,一個非常強大的物件,以數種有用的方法的執行個體。 [圖 4] 會顯示 Shell.Application 物件的成員。

[圖 4 Shell.Application 物件的成員
名稱 MemberType 定義
AddToRecent 方法 void AddToRecent (Variant 型別,字串)
BrowseForFolder 方法 資料夾 BrowseForFolder (int、 string,int,Variant)
CanStartStopService 方法 Variant CanStartStopService (字串)
CascadeWindows 方法 void CascadeWindows ()
ControlPanelItem 方法 void ControlPanelItem (字串)
EjectPC 方法 void EjectPC ()
探索 方法 void 的探索 (Variant 型別)
ExplorerPolicy 方法 Variant ExplorerPolicy (字串)
FileRun 方法 void FileRun ()
FindComputer 方法 void FindComputer ()
FindFiles 方法 void FindFiles ()
FindPrinter 方法 void FindPrinter 字串、 字串 (字串)
GetSetting 方法 bool GetSetting (int)
GetSystemInformation 方法 Variant GetSystemInformation (字串)
協助 方法 void [說明] ()
IsRestricted 方法 int IsRestricted (字串,string)
IsServiceRunning 方法 Variant IsServiceRunning (字串)
MinimizeAll 方法 void MinimizeAll ()
Namespace 方法 資料夾 NameSpace (Variant 型別)
開啟 方法 void 開啟 (Variant 型別)
RefreshMenu 方法 void RefreshMenu ()
ServiceStart 方法 Variant ServiceStart (字串,Variant)
ServiceStop 方法 Variant ServiceStop (字串,Variant)
SetTime 方法 void SetTime ()
ShellExecute 方法 void ShellExecute (字串,Variant、 變數、 變數、 Variant)
ShowBrowserBar 方法 Variant ShowBrowserBar (字串,Variant)
ShutdownWindows 方法 void ShutdownWindows ()
暫停 方法 void 暫停 ()
TileHorizontally 方法 void TileHorizontally ()
TileVertically 方法 void TileVertically ()
ToggleDesktop 方法 void ToggleDesktop ()
TrayProperties 方法 void TrayProperties ()
UndoMinimizeALL 方法 void UndoMinimizeALL ()
Windows 方法 IDispatch Windows ()
Windows 方法 void Windows ()
應用程式 屬性 IDispatch 應用程式 () 取得}
父代 屬性 IDispatch 父 () 取得}

因為您會要重複使用的 COM 物件名稱最好將 COM 物件的程式識別碼指派給變數。 您將可以提供意見反應給使用者時使用字串,新物件的 Cmdlet 以及。 以下是指派 [shell.application 的程式識別碼] 字串給程式碼行。

{
 $comObject = "Shell.Application"

若要提供意見反應,您可以使用 Write-Debug Cmdlet,以嘗試建立 shell.Application 物件的訊息:

 Write-Debug "Creating $comObject"

接下來我們實際上會建立物件:

 $shell = New-Object -Comobject $comObject

然後,我們測試的錯誤。 如果要執行這項操作中,,您可以使用自動變數 $?,它會告訴您,最後一個指令如果順利完成。 這是布林值 True / False。 您可以使用這項事實,以簡化撰寫的程式碼。 您可以使用不運算子,!,搭配 If 陳述式。 如果變數不為真,您可以使用擲回陳述式引發錯誤,停止在的指令碼的執行,這種方式:

 if(!$?) { $(Throw "unable to create $comObject object")}

如果指令碼已成功建立 shell.Application 物件,我們會提供一些意見反應:

 Write-Debug "Creating source cab object for $cab"

下一個步驟在作業中的會是連線至.cab 檔案。 要執行這項操作,您可以使用 Namespace 方法從 shell.Application 物件。 這是另一個重要的步驟,因此合理使用另一個寫入偵錯陳述式做為一個進度指示器使用者:

 $sourceCab = $shell.Namespace($cab).items()
 Write-Debug "Creating destination folder object for $destination"

現在,我們連線到目的地資料夾。 要執行這項操作,您會使用命名空間方法,],然後另一個寫入偵錯陳述式,讓使用者知道哪個資料夾實際上已連接到:

 $DestinationFolder = $shell.Namespace($destination)
 Write-Debug "Expanding $cab to $destination"

所有的準備開,是有點 anticlimactic 實際命令展開封包檔。 您要使用 CopyHere) 方法,從儲存在 $ DestinationFolder 變數中的資料夾物件。 您可以提供.cab 檔案儲存在 $ sourceCab 變數做為輸入參數這種方式參考:

 $DestinationFolder.CopyHere($sourceCab)
}

開始點到指令碼會進行兩件事。 首先,它會檢查 $ 偵錯變數的存在。 如果 $ 偵錯,它會設定在 $ debugPreference 繼續強制 Write-Debug Cmdlet 列印訊息至主控台視窗。 第二個,它會呼叫 ConvertFrom-封包函式並將路徑傳遞至封包檔,從 –cab 命令列參數和擴充的檔案,從 –destination 參數的目的地:

if($debug) { $debugPreference = "continue" }
ConvertFrom-Cab -cab $cab -destination $destination

當您在偵錯模式中執行 ExpandCab.ps1 指令碼時,您會看見類似的輸出 [圖 5 .

fig05.gif

[圖 5] 執行 ExpandCab.ps1 在偵錯模式中

使用 makecab.exe 公用程式

如果您是在 Windows Server 2003 或 Windows XP 上執行這些兩個指令碼,請就不需要任何的問題,但是 Makecab.Makecab COM 物件不存在,Windows Vista 或更新版本。 這個 misfortune 不過,無法停止,決定的 Scripter,因為您永遠可以使用 makecab.exe 從命令列公用程式。 若要這麼做時,您可以使用 [圖 6 ] 所示 CreateCab2.ps1 指令碼。

[圖 6 CreateCab2.ps1

Param(
  $filepath = "C:\fso", 
  $path = "C:\fso1\cabfiles",
  [switch]$debug
  )
Function New-DDF($path,$filePath)
{
 $ddfFile = Join-Path -path $filePath -childpath temp.ddf
 Write-Debug "DDF file path is $ddfFile"
 $ddfHeader =@"
;*** MakeCAB Directive file
;
.OPTION EXPLICIT      
.Set CabinetNameTemplate=Cab.*.cab
.set DiskDirectory1=C:\fso1\Cabfiles
.Set MaxDiskSize=CDROM
.Set Cabinet=on
.Set Compress=on
"@
 Write-Debug "Writing ddf file header to $ddfFile" 
 $ddfHeader | Out-File -filepath $ddfFile -force -encoding ASCII
 Write-Debug "Generating collection of files from $filePath"
 Get-ChildItem -path $filePath | 
 Where-Object { !$_.psiscontainer } |
 ForEach-Object `
 { 
 '"' + $_.fullname.tostring() + '"' | 
 Out-File -filepath $ddfFile -encoding ASCII -append
 }
 Write-Debug "ddf file is created. Calling New-Cab function"
 New-Cab($ddfFile)
} #end New-DDF

Function New-Cab($ddfFile)
{
 Write-Debug "Entering the New-Cab function. The DDF File is $ddfFile"
 if($debug)
 { makecab /f $ddfFile /V3 }
 Else
 { makecab /f $ddfFile }
} #end New-Cab

# *** entry point to script ***
if($debug) {$DebugPreference = "continue"}
New-DDF -path $path -filepath $filepath

與其他指令碼 CreateCab2.ps1 首先會建立數個命令列參數:

Param(
  $filepath = "C:\fso", 
  $path = "C:\fso1\cabfiles",
  [switch]$debug
  )

當您在使用 –debug 參數執行指令碼時,輸出會顯示在類似 [圖 7 .

fig07.gif

[圖 7] 執行 CreateCab2.ps1 在偵錯模式中

接下來,指令碼會建立在新 DDF 的函式,會建立一個基本的.ddf 檔案 MakeCab.exe 程式用來建立.cab 檔案。 中會說明這些類型檔案的語法, Microsoft Cabinet Software Development Kit. 建立新 DDF 的函式使用函式關鍵字之後,您可以使用路徑加入的程式建立暫存的.ddf 檔案,檔案路徑。 您無法在磁碟機 」、 「 的資料夾和 「 檔名一起,串連,但這可能是在麻煩又容易出錯的作業]。 最佳的作法是您永遠都應該使用加入的路徑程式建置您的檔案路徑,這種方式:

Function New-DDF($path,$filePath)
{
 $ddfFile = Join-Path -path $filePath -childpath temp.ddf

如果指令碼會執行與使用者提供的意見反應,— 偵錯 [參數]、 [使用 Write-Debug Cmdlet,如下所示:

 Write-Debug "DDF file path is $ddfFile"

現在您必須建立.ddf 檔案的第一個的部分。 如果要執行這項操作,您可以使用的擴充此處-字串,這表示您不需要考慮自己的逸出特殊字元。 例如,.ddf 檔案中的註解的開頭使用分號是 Windows PowerShell 中的保留的字元。 如果您是嘗試建立此文字,在這裡-字串沒有您必須逸出每一個分號,以避免編譯時期錯誤。 利用擴充此處-字串您可以利用變數的擴充。 在這裡的字串會從開始,使用連字號 (&) 和一個引號,並結束引號和連字號:

 $ddfHeader =@"
;*** MakeCAB Directive file
;
.OPTION EXPLICIT      
.Set CabinetNameTemplate=Cab.*.cab
.set DiskDirectory1=C:\fso1\Cabfiles
.Set MaxDiskSize=CDROM
.Set Cabinet=on
.Set Compress=on
"@

接下來您可以透過 Write-Debug Cmdlet 新增意見反應:

 Write-Debug "Writing ddf file header to $ddfFile" 

現在我們來部分可能會造成一些問題。 .ddf 檔案必須是純 ASCII。 預設的情況下,Windows PowerShell 會使用 Unicode。 為了確保您有 ASCII 檔案,您必須使用 out-file Cmdlet。 您可以避免大部分時間,使用檔案重新導向箭頭,使用 out-file,但這不是這些情況下的其中一個。 以下的語法:

 $ddfHeader | Out-File -filepath $ddfFile -force 
-encoding ASCII

可能要提供寫入偵錯一些詳細偵錯資訊之前,您透過 Get-ChildItem Cmdlet 收集您所收藏的檔案:

 Write-Debug "Generating collection of files from $filePath"
 Get-ChildItem -path $filePath | 

很重要,以篩選出資料夾,從集合中,因為 MakeCab.exe 無法壓縮資料夾。 若要這麼做,使用 [Where-Object 在不使用運算子,指出物件不是容器:

 Where-Object { !$_.psiscontainer } |

接下來,您需要使用每個檔案,是來自透過管線。 要執行這項操作,使用 [ForEach-Object Cmdlet。 因為 ForEach-Object Cmdlet 相對於語言陳述式,大括號必須在相同的一行,做為 ForEach-Object Cmdlet 的名稱。 這個問題,是它有一個傾向以 bury 大括弧,程式碼中。 最佳的作法是,我喜歡對齊,大括號,除非命令是很短,像前一個 Where-Object 指令。 要但是,需要使用行接續字元 (backtick)。 我知道部分的開發人員避免像在的 plague 的行接續符號,但我認為地對齊這兩個大括號位置是更重要,因為它會使程式碼更容易閱讀]。 以下是 ForEach-Object Cmdlet 的開頭:

 ForEach-Object `

因為使用 MakeCab.exe.ddf 檔案 ASCII 文字,您必須轉換 System.IO.Fileinfo 物件傳回字串,Get-ChildItem Cmdlet,fullname 屬性。 此外,由於您可能有以其名稱中空格的檔案,它會單色應該將括一組引號括住的檔案 fullname 值:

 { 
 '"' + $_.fullname.tostring() + '"' | 

您再管線,檔案名稱,以在 out-file Cmdlet,確定指定 ASCII 編碼方式,而使用 –append 參數以避免並覆寫所有其他文字檔中:

 Out-File -filepath $ddfFile -encoding ASCII -append
 }

現在您可以提供在偵錯] 使用者,其他的更新程式,並再呼叫新的封包的函式:

 Write-Debug "ddf file is created. Calling New-Cab function"
 New-Cab($ddfFile)
} #end New-DDF

當您輸入新的封包的函式時,您也可以提供使用者的資訊:

Function New-Cab($ddfFile)
{
 Write-Debug "Entering the New-Cab function. The DDF File is $ddfFile"

接下來,如果指令碼會執行使用 –debug 參數,您可以使用 MakeCab.exe 的 / V 參數,提供詳細的偵錯資訊 (3 是完整的詳細等級 ; 0 為無)。 如果指令碼無法執行,使用 –debug 參數,您不要干擾使用太多的資訊畫面,所以您應該堅持使用公用程式的預設值:

 if($debug)
 { makecab /f $ddfFile /V3 }
 Else
 { makecab /f $ddfFile }
} #end New-Cab

進入點給指令碼檢查 $ 偵錯的變數存在。 其是否在 $ debugPreference 」 自動變數設定為 [繼續],並偵錯資訊會顯示透過 Write-Debug Cmdlet。 已執行的檢查之後,新 DDF Cmdlet 會呼叫兩個值提供給命令列: 路徑,並在 filepath:

if($debug) {$DebugPreference = "continue"}
New-DDF -path $path -filepath $filepath

也,就是它本月的文章,壓縮和 uncompressing 檔案上。 如果您覺得你的頭即將分解,您可以嘗試將 [您 Zune] 插入您的 ear 中,然後查看如果您可以執行這些指令碼來壓縮您的腦的一部分之一。 But…I 告訴您右 now…it 只能在電影。 很抱歉。 如果您沒有設定進行鎖定,則您應該停止 TechNet 指令碼中心,並檢查出我們每天的 「 嗨,Scripting Guy ! > 文件。 請參魷 \ cs6 \ f1 \ cf6 \ lang1024 您有。

Ed Wilson ,一個已知的指令碼專業人員是八個包括 Windows PowerShell Scripting Guide (2008) 的書籍和 Microsoft Windows PowerShell Step by Step 的作者 (2007)。 Ed 會保留 20 個以上的產業認證包括 Microsoft Certified Systems Engineer (MCSE) 以及認證資訊系統安全性專業 (CISSP)。 在他備用的時間,他樂於 woodworking underwater 相片,、 scuba 投擲。 並 tea。

Craig Liebendorfer 是 wordsmith 和 longtime Microsoft Web 的編輯器)。 Craig 仍然無法相信還有這麼他使用字每一天的工作。 其中一個他喜愛的事物是 irreverent 幽默的因此他應該符合在這裡的權限。 他會認為要他 magnificent 女兒生活中的他最大叫好。