Ei, Scripting Guy! Criando os arquivos CAB com o Windows PowerShell

The Microsoft Scripting Guys

Conteúdo

Expandindo arquivos CAB
Usando o utilitário makecab.exe

um dos meus filmes favoritos ("Joaninha mneumônico") foi sobre um rapaz quem era um smuggler dados no futuro. Ele poderia viajar para vários países e atender aos clientes. Ele tinha uma tomada surgically implanted para a base da sua cabeça e os clientes seriam conecte seu dispositivo procurando Zune as quantidades grande tomada e transferência de dados em seu cérebro. Porque este smuggler dados tinha seu cérebro particionado e evidently estava usando permissões de arquivos NTFS, ele não tinha direitos para parte de seu cérebro indesejadas de dados. Os dados, portanto, eram seguros — mesmo do smuggler próprio wandering idéias. Em uma cena, o cliente quisê-lo courier uma grande quantidade de dados, mas evidently nosso smuggler não possui uma capacidade grande o suficiente para transportar dados muito. Então, o que foi ele fazer? Ele compactado seu cérebro — adoro ele!

Compactação de dados não apenas fodder boa para filmes, é igualmente útil para administradores de rede. Nós todos sabe e provavelmente usar, vários utilitários de compactação de arquivos. Usá-los toda semana no envio capítulos do meu novo livro por email para meu editor na Microsoft Press. Fazer isso não muito porque estou largura de banda restrita, mas porque temos as cotas de email que restringir o tamanho de e-mensagens que pode armazenar, enviar e receber. Também usei utilitários de compactação de arquivo quando eu necessário arquivar os arquivos do meu laptop para um dos vários discos portátil flutuante ao redor do meu escritório.

Quando usei viagem em todo o mundo ensinar aos scripts workshops, poderia ser perguntei rotineiramente, "como pode que compactar os arquivos por meio de scripts?" E eu poderia responder, "você precisa comprar um utilitário de terceiros que oferece suporte às opções da linha de comando." Um dia, eu estava lendo por meio do registro, e eu executei em um objeto COM chamado (interessante) makecab.makecab. Suponha Hmm, o que é que esse objeto? Sim, você está certo. Permite que você disponibilizar os arquivos de gabinete (.cab) — altamente compactados arquivos usados por vários aplicativos para o empacotamento e a implantação. Mas não há nada para interromper um administrador de rede enterprising ou especialista em scripts de appropriating essas ferramentas para si mesmos. E isso é apenas o que faremos. Vamos começar com o script na Figura 1 .

A Figura 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

A primeira coisa que precisamos fazer é criar alguns parâmetros de linha de comando usando a instrução de parâmetros. A instrução de parâmetros deve ser a primeira linha noncommented no script. Quando o script é executado de dentro do console do Windows PowerShell ou de dentro de um editor de scripts, os parâmetros de linha de comando são usados para controlar o maneira como o script for executado. Dessa forma, você não precisará editar o script sempre que deseja criar um arquivo .cab de um diretório diferente. Você só precisa fornecer um valor novo para o parâmetro –filepath, conforme mostrado aqui:

A coisa boa sobre parâmetros de linha de comando é que eles usarem conclusão do parâmetro parcial, que significa que você precisa fornecer somente o suficiente do parâmetro para que ela seja exclusivo. Portanto, você pode usar uma sintaxe de linha de comando como este:

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

Essa sintaxe deve pesquisar o diretório c:\fso1 e obter todos os arquivos. Ele, em seguida, deve criar um arquivo de gabinete denominado bcab.cab na pasta fso2 desativar a unidade de c:\. Ele também produziria informações de depuração enquanto ele estava em execução. Observe que o parâmetro –debug é um parâmetro comutado, o que significa que ele afeta o script somente quando ele estiver presente. Aqui está uma seção de script CreateCab.ps1 relevante:

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

Agora vamos criar a função de CAB de novo, que aceita dois parâmetros de entrada, –path e –files:

Function New-Cab($path,$files)

Você pode atribuir a identificação de programa, makecab.makecab para um makecab variável $ nomeado, que tornará o script um pouco mais fácil de ler. Isso também é um bom local para colocar a primeira instrução de Write-Debug:

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

Em seguida, criaremos o objeto COM:

 $cab = New-Object -ComObject $makecab

Um bit de verificação de erros é em ordem, que pode ser feito usando o $? variável automática:

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

Se nenhum erro ocorreu durante a tentativa de criar o objeto makecab.makecab, você pode usar o objeto contido na variável $ cab e chamar o método CreateCab:

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

Depois que o arquivo .cab tem sido criado, você pode adicionar arquivos a ele usando a instrução ForEach:

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

Depois de você ter transformado o nome do arquivo completo em uma seqüência de caracteres e removidos as informações de diretório usando o cmdlet de caminho de divisão, você incluir outra instrução de Write-Debug para permitir que o usuário do script sabe o que está acontecendo, desta forma:

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

Em seguida adicione o arquivo para o arquivo de gabinete:

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

Para fechar o arquivo de gabinete, você use o método CloseCab:

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

Agora vamos até o ponto de entrada do script. Primeiro, verifique se o script está sendo executado no modo de depuração, procure a variável de depuração $. Se a variável de depuração $ não estiver presente, você não precisará fazer nada. Se ele estiver presente, você precisará definir o valor da $ DebugPreference variável para continuar, que permite que as instruções Write-Debug a ser impresso na tela. Por padrão, $ DebugPreference é definido para silentlycontinue, que significa ignorar o comando sem fazer nada. Eis o código:

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

Agora você precisa obter uma coleção de arquivos. Para fazer isso, você pode use o cmdlet Get-ChildItem:

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

Em seguida, passe a coleção para a função New-CAB, conforme mostrado aqui:

New-Cab -path $path -files $files

Quando você executa o script CreateCab.ps1 no modo de depuração, você verá saída conforme mostrado na A Figura 2 .

fig02.gif

A Figura 2 CreateCab.ps1 em execução no modo de depuração

Expandindo arquivos CAB

Você não pode usar o objeto de makecab.makecab para expandir o arquivo de gabinete porque ele não tem um método de expansão. Nem pode você usar o makecab.expandcab objeto porque ele não existe. Mas a capacidade de expandir um arquivo de gabinete é inerente no shell do Windows, para poder usar o objeto shell. Para acessar o shell, você pode usar o objeto Shell.Application COM, como mostra o script ExpandCab.ps1 na Figura 3 .

A Figura 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

Primeiro o script cria os parâmetros da linha de comando, assim como CreateCab.ps1 foi:

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

Em seguida ele cria a ConvertFrom-cab função, que aceita dois parâmetros de linha de comando, uma que contém o arquivo .cab e outra que contém o destino para expandir os arquivos:

Function ConvertFrom-Cab($cab,$destination)

Agora você pode criar uma instância do objeto Shell.Application, um objeto muito eficiente com um número de métodos úteis. a Figura 4 mostra os membros do objeto Shell.Application.

Figura 4 membros de objeto Shell.Application
Nome MemberType Definição
AddToRecent Método Cancelar AddToRecent (Variant, seqüência de caracteres)
BrowseForFolder Método Pasta BrowseForFolder (int, seqüência de caracteres, int, variante)
CanStartStopService Método Variante CanStartStopService (seqüência)
CascadeWindows Método Cancelar (CascadeWindows)
ControlPanelItem Método ControlPanelItem void (seqüência)
EjectPC Método Cancelar (EjectPC)
Explore Método Explorar void (variante)
ExplorerPolicy Método Variante ExplorerPolicy (seqüência)
FileRun Método Cancelar (FileRun)
FindComputer Método Cancelar (FindComputer)
FindFiles Método Cancelar (FindFiles)
FindPrinter Método Cancelar FindPrinter (seqüência de caracteres, seqüência de caracteres, seqüência de caracteres)
GetSetting Método bool GetSetting (int)
GetSystemInformation Método Variante GetSystemInformation (seqüência)
Ajuda Método Cancelar (Ajuda)
IsRestricted Método int IsRestricted (seqüência de caracteres, seqüência de caracteres)
IsServiceRunning Método Variante IsServiceRunning (seqüência)
MinimizeAll Método Cancelar (MinimizeAll)
NameSpace Método NameSpace de pasta (variante)
Abrir Método Abrir void (variante)
RefreshMenu Método Cancelar (RefreshMenu)
ServiceStart Método Variante ServiceStart (seqüência, variante)
ServiceStop Método Variante ServiceStop (seqüência, variante)
SetTime Método Cancelar (SetTime)
ShellExecute Método Cancelar ShellExecute (seqüência, Variant, Variant, Variant, Variant)
ShowBrowserBar Método Variante ShowBrowserBar (seqüência, variante)
ShutdownWindows Método Cancelar (ShutdownWindows)
Suspender Método Cancelar (Suspend)
TileHorizontally Método Cancelar (TileHorizontally)
TileVertically Método Cancelar (TileVertically)
ToggleDesktop Método Cancelar (ToggleDesktop)
TrayProperties Método Cancelar (TrayProperties)
UndoMinimizeALL Método Cancelar (UndoMinimizeALL)
Windows Método IDispatch Windows)
WindowsSecurity Método Cancelar (WindowsSecurity)
Aplicativo Propriedade Aplicativo de IDispatch () {get}
Pai Propriedade Pai de IDispatch () {get}

Porque você quer usar o nome do objeto COM mais de uma vez, é uma boa prática para atribuir a identificação de programa do objeto COM a uma variável. Você poderá usar a seqüência de caracteres com o cmdlet New-Object, bem como ao fornecer comentários para o usuário. Aqui está a linha de código que atribui a identificação do programa Shell.Application a uma seqüência de caracteres.

{
 $comObject = "Shell.Application"

Para fornecer comentários, você pode usar o cmdlet Write-Debug com uma mensagem que você está tentando criar o objeto Shell.Application:

 Write-Debug "Creating $comObject"

Em seguida, na verdade, vamos criar o objeto:

 $shell = New-Object -Comobject $comObject

Então, testar para erros. Para fazer isso, você pode usar a variável $ automática?, que informa se o último comando concluída com êxito. É um booleano verdadeiro/falso. Você pode usar esse fato para simplificar a codificação. Você usa o não operador,!, em conjunto com um se instrução. Se a variável não for verdadeira, você usa a declaração Throw para gerar um erro e interromper a execução do script, desta forma:

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

Se o script com êxito cria o objeto Shell.Application, fornecemos alguns comentários:

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

A próxima etapa na operação é conectar ao arquivo .cab. Para fazer isso, você pode use o método espaço para nome do objeto Shell.Application. Essa é outra etapa de importante, para que ele faz sentido usar outra instrução de Write-Debug como um indicador de progresso para o usuário:

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

Agora se conectar à pasta destino. Para fazer isso, use o método de espaço para nome e outra instrução de Write-Debug para permitir que o usuário sabe qual pasta, na verdade, estava conectada:

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

Com todos os que preparação fora do caminho, o comando real para expandir o arquivo de gabinete é um pouco anticlimactic. Use o método CopyHere do objeto de pasta que é armazenado na variável $ DestinationFolder. Você conceder a referência ao arquivo .cab armazenados na variável $ sourceCab como o parâmetro de entrada dessa forma:

 $DestinationFolder.CopyHere($sourceCab)
}

O ponto de partida para o script faz duas coisas. Primeiro, ele verifica a presença da variável $ depuração. Se $ depuração estiver presente, ele define o debugPreference $ para continuar a forçar o cmdlet Write-Debug para imprimir mensagens para a janela de console. Em segundo lugar, ele chama a função ConvertFrom-CAB e passa o caminho para o arquivo cab do parâmetro de linha de comando –cab e o destino para os arquivos expandidos do parâmetro –destination:

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

Quando você executa o script de ExpandCab.ps1 no modo de depuração, você verá o resultado semelhante ao que, em A Figura 5 .

fig05.gif

A Figura 5 ExpandCab.ps1 em execução no modo de depuração

Usando o utilitário makecab.exe

Se você executar esses dois scripts no Windows Server 2003 ou no Windows XP, você não terá quaisquer problemas, mas o objeto COM makecab.makecab não existe no Windows Vista ou posterior. Este misfortune não pára o criador de scripts determinado, no entanto, porque você sempre pode usar o utilitário makecab.exe a partir da linha de comando. Para fazer isso, você pode usar o script de CreateCab2.ps1, mostrado na Figura 6 .

A Figura 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

Como com os outros scripts, CreateCab2.ps1 primeiro cria alguns parâmetros de linha de comando:

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

Quando você executa o script com a opção –debug, a saída será semelhante à que é mostrado na A Figura 7 .

fig07.gif

A Figura 7 CreateCab2.ps1 em execução no modo de depuração

Em seguida, o script cria a função de New-DDF, que cria um arquivo .ddf básico que é usado pelo programa MakeCab.exe para criar o arquivo .cab. A sintaxe para esses tipos de arquivos está documentada no Arquivo de gabinete Microsoft Software Development Kit. Depois de usar a palavra-chave de função para criar a função de New-DDF, você usar o cmdlet de caminho de associação para criar o caminho de arquivo para o arquivo .ddf temporário. Você pode concatenar a unidade, a pasta e o nome do arquivo juntos, mas isso pode ser uma operação ineficiente e propenso a erros. Como prática melhor, você deve sempre use o cmdlet de caminho de associação para criar seus caminhos de arquivo, dessa forma:

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

Para fornecer um pouco de comentários para o usuário se o script é executado com o — opção de depuração, use o cmdlet Write-Debug como mostrado aqui:

 Write-Debug "DDF file path is $ddfFile"

Agora você precisará criar a primeira parte do arquivo .ddf. Para fazer isso, você pode usar uma expansão aqui-cadeia de caracteres, que significa que você não terá que se referem-se com caracteres especiais de saída. Como exemplo, os comentários em um arquivo .ddf são prefaceados com um ponto-e-vírgula, que é um caractere reservado no Windows PowerShell. Se você tentar criar esse texto sem um aqui-cadeia de caracteres, você precisará sair cada o ponto-e-vírgula para evitar erros em tempo de compilação. Usando uma expansão aqui-cadeia de caracteres, você pode aproveitar a expansão de variáveis. Um aqui-cadeia de caracteres começa com o e comercial e uma aspa e termina com aspas e um e comercial:

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

Em seguida, é aconselhável adicionar comentários via o cmdlet Write-Debug:

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

Agora, chegamos à parte que poderia causar alguns problemas. O arquivo .ddf deve ser ASCII puro. Por padrão, o Windows PowerShell usa Unicode. Para garantir que você tenha um arquivo ASCII, você deve usar o Out-File cmdlet. Na maioria das vezes, você pode evitar usando Out-File usando as setas de redirecionamento de arquivo, mas isso não é um das ocasiões. Aqui está a sintaxe:

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

Você provavelmente deseja fornecer algumas informações de depuração mais com Write-Debug antes de você obter sua coleção de arquivos por meio do cmdlet Get-ChildItem:

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

É importante filtrar pastas a partir da coleção porque MakeCab.exe não é capaz de compactar pastas. Para fazer isso, use o Where-Object com uma não o operador informando que o objeto não é um contêiner:

 Where-Object { !$_.psiscontainer } |

Em seguida, você precisará trabalhar com cada arquivo como ele encontra o pipeline. Para fazer isso, use o cmdlet ForEach-Object. Porque ForEach-Object é um cmdlet, oposição a uma instrução de linguagem, as chaves devem estar em mesma linha como o nome do cmdlet ForEach-Object. O problema com isso é que ele tem uma tendência a encobrir as chaves no código. Como prática recomendada, gosto alinhar as chaves, a menos que o comando seja muito curto como o comando Where-Object anterior. Para fazer isso, entretanto, requer o uso do caractere de continuação de linha (backtick). Eu sei alguns desenvolvedores que evitar continuação de linha como o plague, mas eu acho que alinhar chaves é mais importante porque ela torna o código mais fácil de ler. Aqui é o começo do cmdlet ForEach-Object:

 ForEach-Object `

Como o arquivo .ddf usado pelo MakeCab.exe é um texto de ASCII, será necessário converter a propriedade fullname do objeto System.IO.fileinfo retornado pelo cmdlet Get-ChildItem em uma seqüência de caracteres. Além disso, porque você pode ter arquivos com espaços nos nomes, ele faz sentido colocar o valor de fullname do arquivo em um conjunto de aspas:

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

Você pipeline, em seguida, os nomes de arquivos para o Out-File cmdlet, certificando-se para especificar a codificação ASCII e usar a opção de –append para evitar a substituição tudo no arquivo de texto:

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

Agora você pode fornecer outra atualização para os usuários de depuração e, em seguida, chamar a função de New-CAB:

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

Quando você insere a função de New-CAB, você também pode fornecer essas informações para o usuário:

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

Em seguida, se o script for executado com a opção –debug, podem usar /V parâmetros do MakeCab.exe para fornecer informações de depuração detalhadas (3 é o detalhamento completo; é 0 nenhum). Se o script não é executado com a opção –debug, não deseja truncar a tela com muitas informações para que você deve continuar com padrão o utilitário:

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

O ponto de entrada para o script verifica ver se o $ depurar variável está presente. Se for, a variável $ debugPreference automática será definido para continuar, e as informações de depuração serão exibida via o cmdlet Write-Debug. Após ter sido realizada essa seleção, o cmdlet New-DDF é chamado com os dois valores fornecidos para a linha de comando: o caminho e o caminho de arquivo:

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

Bem, que é sobre isso para o artigo deste mês na compactação e descompactação de arquivos. Se você sentir como cabeça está prestes a Detalhar, pode tentar conecte o Zune o ano e ver se você pode executar um desses scripts para compactar parte do seu cérebro. But…I estou dizendo now…it direita funciona apenas nos filmes. Resposta incorreta. Se você não bloquear backup, você deve parar, o Script Center da TechNet e confira nosso diária "Ei, Equipe de Scripts!" artigo. Consulte você não existe.

Ed Wilson , uma especialista em scripts conhecida, é autor do oito livros, incluindo Windows PowerShell Scripting Guide (2008) e Microsoft Windows PowerShell Step by Step (2007). Ed contém mais de 20 certificações do setor, incluindo Microsoft Certified Systems Engineer (MCSE) e o Professional de Security Systems (CISSP) de informações do certificado. Em seu tempo livre, ele goza woodworking, fotografia submarinas e scuba diving. E Chá.

Craig Liebendorfer é um wordsmith e longtime editor da Microsoft. Craig ainda não acredito há um trabalho que paga-lo para trabalhar com palavras todos os dias. Uma das suas coisas favoritas é irreverent humor, para que ele deve ajustar direita em aqui. Ele considera seu accomplishment maior na vida seja sua filha magnificent.