Powers Hell

65
Simplificando tarefas usando PowerShell Quem ainda não conhece ou nunca trabalhou com PowerShell? Agora, com certeza vai começar a usar. Vamos reunir aqui nos artigos da comunidade WinINFO uma sessão de dicas de uso do PowerShell, apresentando é claro, os scripts que podem ajudar a você (administrador/usuários) automatizar e acelerar tarefas diárias, principalmente as tarefas que são repetidas constantemente. O PowerShell evoluiu para melhorar e reforçar sua performance. Já está inclusive, disponível para download a versão do 2.0 do PowerShell para Windows XP, Vista, 2003 e 2008. Agora, apresentando a sessão simplificando tarefas com PowerShell, foram criados 3 scripts para demosntrar na prática como o PowerShell pode ajudar você: 1. start-service.ps1 Iniciar determinado serviço # Modelo de uso: .\start-service name_machine name_service param($name , $serv ) gwmi win32_service -comp $name -filter "name like '$serv'" | % {$_.startservice()} |select returnValue #Fim do arquivo 2. check-service.ps1 Checar status de um serviço # Modelo de uso: .\check-service name_machine name_service param($name , $serv ) gwmi win32_service -comp $name -filter "name like '$serv'" | select name,startmode,state,status #Fim do arquivo

Transcript of Powers Hell

Page 1: Powers Hell

Simplificando tarefas usando PowerShell

Quem ainda não conhece ou nunca trabalhou com PowerShell? Agora, com certeza vai começar a usar.Vamos reunir aqui nos artigos da comunidade WinINFO uma sessão de dicas de uso do PowerShell, apresentando é claro, os scripts que podem ajudar a você (administrador/usuários) automatizar e acelerar tarefas diárias, principalmente as tarefas que são repetidas constantemente.O PowerShell evoluiu para melhorar e reforçar sua performance. Já está inclusive, disponível para download a versão do 2.0 do PowerShell para Windows XP, Vista, 2003 e 2008.

Agora, apresentando a sessão simplificando tarefas com PowerShell, foram criados 3 scripts para demosntrar na prática como o PowerShell pode ajudar você:

1. start-service.ps1 

Iniciar determinado serviço

# Modelo de uso: .\start-service name_machine name_serviceparam($name , $serv )gwmi win32_service -comp $name -filter "name like '$serv'" | % {$_.startservice()} |select returnValue#Fim do arquivo

2. check-service.ps1

Checar status de um serviço

# Modelo de uso: .\check-service name_machine name_serviceparam($name , $serv )gwmi win32_service -comp $name -filter "name like '$serv'" | select name,startmode,state,status#Fim do arquivo

3. stop-service.ps1

Parar determinado Serviço

# Modelo de uso: .\stop-service name_machine name_serviceparam($name , $serv )

Page 2: Powers Hell

gwmi win32_service -comp $name -filter "name like '$serv'" | % {$_.stopservice()} |select returnValue#Fim do arquivo

4. space-Disk.ps1

Script para verificar espaço em disco:

# Modelo de uso: .\space-Disk.ps1 nome_maquinafunction Get-DiskSpace($comp) {  $disks = Get-WmiObject Win32_logicaldisk -computer $comp | Where-Object {$_.DriveType -eq 3} | Select-Object Name,@{Name="Size";Expression={"{0:f} GB" -f ($_.Size/1GB)}},@{Name="FreeSpace";Expression={"{0:f} GB" -f ($_.FreeSpace/1GB)}}  $disks}if ($args[0] -ne $null) {  Get-DiskSpace $args[0] | fl}

5. check-bios.ps1

Script para verificar a versão da bios do seu computador ou outros computadores, dependendo do seu ambiente.Primeiro crie a lista: lista_checkbios.txt, dentro desse arquivo .txt coloque o nome_da_máquina que você deseja verificar. Exemplo: 'localhost'.Execute o comando:

# Modelo de uso: .\check-bios.ps1gwmi win32_bios -cn (gc lista_checkbios.txt) |select __server, SMBIOSBIOSVersion, Manufacturer, Name, SerialNumber, version

6. Get-LastBootTime.ps1

Esse script verifica o último boot da sua máquina, dependendo do seu ambiente de outras máquinas também.

# Modelo de uso: .\Get-LastBootTime.ps1 name_machine

Page 3: Powers Hell

param($comp='', [switch]$relative)if (-not $comp) {    $comp = $input}

if (-not $comp) {    throw "Servidor não especificado!"}

$now = Get-Dateforeach ($comp in $comp) {    $wmi = Get-WmiObject -class win32_operatingsystem -namespace root\cimv2 `        -computername $comp    $lastBootTime = $wmi.ConvertToDateTime($wmi.LastBootUpTime)    if ($relative) {        "$comp- $($now - $lastBootTime)"    } else {        "$comp - $lastBootTime"    }}

Se quiser obter o resultado de outras máquinas, basta acresentar no modelo de uso as demais máquinas que você possui no seu ambiente, exemplo:

.\Get-LastBootTime.ps1 name_machine1, name_machine2, name_machine3

Sendo assim, você terá um relatório completo e sem fazer esforço. Esses Scripts tem como objetivo facilitar e automatizar diversas tarefas, como essa.

Do Windows PowerShell: Curso rápido de scripts

Esta abrangente coluna sobre o Windows PowerShell o introduzirá a essa poderosa tecnologia.

Don Jones Mais de vocês são acostumar Windows PowerShell e concretizar suas vantagens. Com isso em mente, coluna deste mês vai ser um longo. Esta é uma visão geral sobre lightning Windows PowerShell script, incluindo como criar scripts com parâmetros. Nos próximos meses, falarei sobre tópicos específicos que se baseiam nessa base.

Se você não esteja habituado a executar os comandos de Windows PowerShell no console, você pode encontrar essa muito avançada, mas tentar vasculhar assim mesmo. Você deve

Page 4: Powers Hell

ter uma compreensão completa dos recursos de segurança Windows PowerShell. Você já deve saber sobre a diretiva de execução e saber qual configuração você está usando. Se você ainda não saiba a diferença entre "RemoteSigned" e "AllSigned" e por que um pode ser melhor do que o outro, você talvez não esteja pronta para o material a seguir.

Você também deve saber como executar scripts no shell e deve se lembrar de que você sempre deve fornecer um caminho e nome de arquivo para executar um script. Finalmente, você também deve saber a diferença entre executar um script no Integrated Scripting Environment (ISE) e no console. No ISE, os scripts são executados no escopo global. No console do shell normal, os scripts de obtém seu próprio escopo. Vamos analisar escopo, mas você já deve ter uma idéia do que significa e como funciona.

Se você não se bastante deleite, dar uma olhada no meu livro, "Saiba de Windows PowerShell em um mês de almoços" (Manning Publications, 2011) e o companion Web site da e veja se esses recursos podem ajudá-lo a construir uma base melhor.

Tente acompanhá-lo enquanto você lê essa coluna. Testar os exemplos. Se você digitar (ou copie e cole) os exemplos de script em Windows PowerShell ISE começando na linha 1, seus números de linha irá corresponder com os números de linha nas descrições.

Arquivos de Script Windows PowerShell

Um arquivo de script Windows PowerShell é nada mais que um arquivo de texto sem formatação que tenha um.Extensão de nome de arquivo ps1. O "1" não se refere a versão do Windows PowerShell, mas em vez disso, a versão do mecanismo do idioma. Windows PowerShell versão 1 e 2 use engine versão 1 do idioma. É por isso que as duas versões do shell são instalados em uma pasta de versão 1.0 em \Windows\System32\WindowsPowerShell.

Um script de Windows PowerShell não é exatamente como um arquivo em lotes de linha de comando e executar um script não é precisamente a mesma que executar os mesmos comandos na mesma seqüência. Por exemplo, abra uma janela de console e execute o seguinte, pressionando Enter após cada linha (Lembre-se não digite os números de linha):

Get-ServiceGet-Process

Agora digite essas mesmas linhas exatas em um arquivo de script ou o script ISE, painel de edição e execute o script. Você obterá resultados de busca diferentes. Cada vez que você pressione a tecla Enter em Windows PowerShell, que você iniciar um novo pipeline. Quaisquer comandos que você digitou são executados desse pipeline único. No final do pipeline, o Windows PowerShell converte seu conteúdo em uma exibição de texto. Quando você executar os dois comandos no console do normal, tiver feito isso em duas tubulações distintas.

Windows PowerShell foi possível construir uma exibição exclusiva para cada conjunto de saída. Quando inserido em um script, no entanto, ambos os comandos executados no mesmo pipeline. O Windows PowerShell sistema de formatação não seja sofisticada o

Page 5: Powers Hell

suficiente para construir a mesma exclusivo saída para dois conjuntos diferentes de resultados. Tente executar isso no console:

Get-Service;Get-Process

Esses resultados devem parecer igual, da mesma forma que quando você executou o script que contém esses dois comandos. Nesse caso, ambos os comandos executados em uma tubulação única. Isso é o que aconteceu quando você executou o script.

O upshot prático de todos os isso é que um script deve produzir apenas um tipo de saída. É uma má idéia devido em grande parte das limitações de sistema de formatação. Existem outras considerações. Você não quer um script despejando vários tipos de coisas no pipeline, ao mesmo tempo.

Concentrar nela como uma regra para tudo, que vamos abordar. Um script deve gerar um e somente um tipo de saída. A única exceção seria se ele é um script que está sendo usado como um repositório para várias funções. Nesse caso, cada função deve gerar um e somente um tipo de saída.

Variáveis

Pense em variáveis como uma caixa. Você pode colocar um ou mais itens, até mesmo coisas diferentes, nessa caixa. A caixa tem um nome e Windows PowerShell esse nome pode incluir em praticamente qualquer coisa. "Var" pode ser um nome de variável, como can "{minha variável}". No segundo exemplo, as primeiras chaves coloque um nome de variável que contém espaços, que é bastante feio. Como uma boa prática, siga os nomes de variáveis que incluem letras, números e sublinhados.

Usando o nome de uma variável faz referência a toda a "caixa". Se você deseja referenciar o conteúdo da caixa, adicionar um sinal de cifrão: $ var. Você verá com freqüência precedidas do sinal de dólar porque o objetivo de usar um é obter o conteúdo de variáveis de Windows PowerShell. É importante lembrar, no entanto, que o cifrão não é parte do nome da variável. Ele é simplesmente uma indicação para informar ao Windows PowerShell que você deseja que o conteúdo, em vez da própria caixa. Por exemplo:

$var = 'hello'$number = 1$numbers = 1,2,3,4,5,6,7,8,9

Esses exemplos mostram como colocar itens em uma variável usando o operador de atribuição (=). Esse último exemplo cria uma matriz, porque Windows PowerShell interpreta todas as listas separadas por ponto-e-vírgula como uma matriz ou coleção, de itens. O primeiro exemplo atribui a um objeto de seqüência de caracteres, com os caracteres na seqüência de caracteres entre aspas.

Há um aspecto de Windows PowerShell que pode confundir os novatos. Windows PowerShell não "compreender" qualquer significado, que você pode associar um nome de

Page 6: Powers Hell

variável. Uma variável como nome_do_computador $ não "informa" shell do que a variável irá conter um nome de computador.

Da mesma forma, os números de $ não "informam" shell do que uma variável irá conter mais de um número. O shell não se importa se você usar um nome de variável no plural. A instrução

$numbers = 1

é igualmente válida para o shell, como está

$numbers = 'fred.'

Quando uma variável contém vários valores, no entanto, você pode usar uma sintaxe especial para acessar apenas uma única delas. Você usaria os números de $[0] como o primeiro item, números de $[1] é o segundo, números de $-[1] é a última, números de $-[2] é o último segundo e assim por diante.

Aspas

Como prática recomendada, use aspas para delimitar uma variável, a menos que tenha um motivo específico para fazer o contrário. Existem três instâncias específicas em que você desejaria usar aspas duplas.

A primeira é quando você precisa inserir o conteúdo de uma variável em uma seqüência de caracteres. Aspas duplas apenas, Windows PowerShell irá procurar o $ e supondo que tudo que vier depois $, até o primeiro caractere que é ilegal em um nome de variável, é um nome de variável. O conteúdo dessa variável irá substituir o nome da variável e o $:

$name = 'Don'$prompt = "My name is $name"

O prompt $ agora conterá "meu nome é Don" porque o nome de $ será substituído com o conteúdo da variável. Esta é uma excelente dica para combinar seqüências de caracteres sem precisar concatená-las.

Aspas duplas, Windows PowerShell também procure seu caractere de escape, o backtick ou o acento e age de acordo. Aqui estão alguns exemplos:

$debug = "`$computer contains $computer"$head = "Column`tColumn`tColumn"

No primeiro exemplo, o primeiro $ está sendo "perdida." Que remove seu significado especial como um acessador de variável. Se o computador de $ contido 'Servidor' em seguida, depuração de $ conteria "computador $ contém SERVER".

Page 7: Powers Hell

No segundo exemplo, ' t representa um caractere de tabulação horizontal, para que Windows PowerShell colocará uma guia entre cada coluna. Você pode ler sobre outros caracteres de escape especiais no shell do HYPERLINK "http://technet.microsoft.com/library/dd347662.aspx"about_escape_characters tópico da Ajuda.

Por fim, use aspas duplas quando uma cadeia de caracteres deve conter aspas simples:

$filter1 = "name='BITS'"$computer = 'BITS'$filter2 = "name='$computer'"

Neste exemplo, a seqüência de caracteres literal é o nome = 'BITS'. As aspas duplas contêm todo o conteúdo. US $Filtro1 e US $Filtro2 acabam contendo exatamente a mesma coisa, mas a US $Filtro2 de chegar lá, usando o truque de substituição de variável de aspas duplas. Observe que apenas o conjunto mais externo de cotações, na verdade, é importante. As aspas simples dentro da seqüência não importam para Windows PowerShell. As aspas simples são caracteres literais apenas. Windows PowerShell não interpretá-los.

Variáveis e objetos membros

Tudo o que Windows PowerShell é um objeto. Até mesmo uma seqüência de caracteres simple como, por exemplo, "nome" é um objeto, do tipo System. String. Você pode canalizar qualquer objeto para Get-Member para ver seu nome de tipo (ou seja, o tipo de objeto que ele é), bem como seus membros, que inclui suas propriedades e métodos:

$var = 'Hello'$var | Get-Member

Use um período após um nome de variável para informar o shell, "eu não quero acessar o objeto inteiro dentro dessa variável. Quero apenas uma das suas propriedades ou métodos de acesso." Após o período, forneça o nome de propriedade ou método.

Nomes de método são sempre seguidos de um conjunto de parênteses. Alguns métodos aceitam argumentos de entrada e aqueles vá dentro dos parênteses em uma lista separada por vírgulas. Outros métodos não requerem nenhum argumento, e então os parênteses vazios, mas não se esqueça dos parênteses:

$svc = Get-Service$svc[0]. name$name = $svc[1]. name$name.length$name.ToUpper()

Page 8: Powers Hell

Observe a linha dois. Ele é iniciado, acessando o primeiro item na variável $svc. O meio período, "eu não quero que todo o objeto. Queria apenas uma propriedade ou método." Isso acessa a propriedade de nome. A linha cinco ilustra como acessar um método, fornecendo seu nome após um período, seguido de parênteses.

Um período normalmente é um caractere inválido dentro de um nome de variável, porque o período significa que desejamos acessar uma propriedade ou método. Isso significa que a linha dois no exemplo a seguir não funciona da maneira que você poderia esperar:

$service = 'bits'$name = "Service is $service.ToUpper()"$upper = $name.ToUpper()$name = "Service is $upper"

Na linha dois, $nome conterá "o serviço é de BITS.ToUpper()" enquanto na linha quatro $nome conterá "O serviço é BITS".

Parênteses

Além de seu uso com os métodos do objeto, parênteses também atuam como um marcador de ordem de execução, de Windows PowerShell, assim como em álgebra. Em outras palavras, os parênteses informam o shell "executar este primeiro". Toda a expressão entre parênteses é substituída por tudo o que produz dessa expressão. Eis alguns exemplos de mind-bending:

$name = (Get-Service)[0]. nameGet-Service -computerName (Get-Content names.txt)

Na linha um, $nome conterá o nome do primeiro serviço do sistema. Lendo este artigo leva um pouco de esforço. Comece com a expressão entre parênteses. Isso é o que Windows PowerShell começará com também. "Get-Service" resolve para uma coleção ou matriz, dos serviços. A [0] acessa o primeiro item em uma matriz, para que seja o primeiro serviço. Porque ele é seguido por um ponto, nós sabemos que estamos acessando uma propriedade ou método de serviço, em vez do objeto de todo o serviço. Por fim, podemos extrair apenas o nome do serviço.

Na linha dois, a expressão entre parênteses está lendo o conteúdo de um arquivo de texto. Supondo que o arquivo contém um nome de computador por linha, "Get-Content" "irá retornar uma matriz de nomes de computador. Aqueles alimenta o parâmetro "– ComputerName" de "Get-Service". Nesse caso, o shell pode alimentar qualquer expressão entre parênteses que retorna uma matriz de seqüências de caracteres para o o parâmetro "– ComputerName", porque o parâmetro foi projetado para aceitar a matrizes de seqüências.

Page 9: Powers Hell

Escopo

O escopo é um conceito de programação que atua como um sistema de containerization. Coisas como variáveis, aliases, PSDrives e outros elementos de Windows PowerShell são armazenadas em um escopo. O shell mantém uma hierarquia de escopos e tem um conjunto de regras que determinam como os escopos possam interagir e compartilhar informações entre si.

O próprio shell é um único escopo, chamado de escopo global. Quando você executa um script, ele constrói um novo escopo e o script é executado dentro do que. Qualquer texto criado por script, como, por exemplo, uma nova variável, é armazenado dentro do escopo do script. Ele não está acessível pelo shell de nível superior.

Quando o script terminar a execução, seu escopo é descartado e nada criados dentro desse escopo desaparece. Por exemplo, criar um script que contém o seguinte (não se esqueça não digite os números de linha), e execute esse script a partir da janela do console:

New-PSDrive -PSProviderFileSystem -Root C:\ -Name SysDir SYS:

Depois de executar o script, execute manualmente "Dir SYS:" e você verá um erro. Isso ocorre porque o SYS: unidade foi criada no script. Depois que o script foi feito, tudo o que ele criou foi descartado. O SYS: unidade não existe mais. Nem tudo no shell do escopo definido. Itens como módulos são manipuladas globalmente em todas as ocasiões. Um script pode carregar um módulo e o módulo permanecerá carregado após o script é feito.

Se um escopo tenta acessar algo que não tenha sido criado dentro desse escopo, Windows PowerShell procura ao escopo mais alto seguinte (o escopo de "pai"). É por isso que o Dir trabalhou alias que você acabou de script inserido. Embora o Dir não existia no escopo do script, ele existisse no escopo da mais alto seguinte: escopo global. Um escopo é livre para criar um item que tem o mesmo nome de um item de um escopo de nível mais alto, porém. Este é outro script tentar:

DirNew-Alias Dir Get-AliasDir

Que pode parecer estranho, mas na primeira vez que ele foi executado "Dir", ele não existe no escopo do script. Ele usado o alias de Dir de nível mais alto. Alias aponta para Get-ChildItem, portanto, exibido uma listagem de diretório familiar.

Em seguida, o script cria um novo alias nomeado dir. Isso aponta para o Alias de Get. Isso é o que foi executado pela segunda vez. Nada disso afetado o alias de Dir de nível superior. Tente executar o Dir no shell depois de executar o script anterior, e você ainda terá uma listagem de diretório.

Escopo pode ser bastante confuso quando se trata de variáveis. Como regra, um determinado escopo nunca deveria acessar itens fora do escopo, especialmente as variáveis. Há uma sintaxe para fazê-lo, como o uso de $global: var forçosamente acessar a variável do

Page 10: Powers Hell

escopo global $var, mas essa é uma prática ruim, exceto em circunstâncias muito específicas.

Linguagem de script Windows PowerShell

Windows PowerShell contém uma linguagem de script muito simples de palavras-chave de menos de duas dúzias. É um contraste perfeito para uma linguagem de programação completa, como VBScript, que contém mais de 300 palavras-chave.

O idioma de Windows PowerShell simplificado embora, é mais do que suficiente para fazer o trabalho. Vamos analisar suas principais construções scripts agora, embora você sempre pode obter mais ajuda sobre estes lendo o "tópico dentro do shell sobre" apropriado. Por exemplo, about_switchcontains informações de ajuda sobre a construção de Switch, enquanto about_if de Ajuda contém informações sobre a se construir. Executar a Ajuda sobre * para obter uma lista de todos os "tópicos sobre".

Se construir

Esta é a construção de tomada de decisão principal Windows PowerShell. Em sua forma completa, ele é semelhante a:

If ($this -eq $that) { # commands} elseif ($those -ne $them) { # commands} elseif ($we -gt $they) { # commands} else { # commands}

A palavra-chave "If" é uma parte obrigatória para essa construção. Uma expressão entre parênteses segue que deve ser avaliada como True ou False. Windows PowerShell sempre interpretarão o zero como False e qualquer valor diferente de zero como True.

Windows PowerShell também reconhece as variáveis internas $True e False $ como que representa os valores booleanos. Se a expressão entre parênteses gostar como True, os comandos no seguinte conjunto de chaves serão executado. Se a expressão for falso, os comandos não irá executar. Isso realmente é tudo o que você precisa válido, se construir.

Você pode ir um pouco além, fornecendo uma ou mais seções de "ElseIf". Essas funcionam da mesma forma como a se construir. Eles obtêm sua própria expressão entre parênteses. Se for verdadeiro, os comandos dentro dos colchetes chaves seguintes serão executados. Caso contrário, eles não.

Você pode resumir com um bloco Else, que será executado se nenhum dos blocos precedentes executar. Apenas o bloco associado com a primeira expressão verdadeira será executada. Por exemplo, se $isso não ser igual a $, e $aqueles não igual $-los, em seguida, os comandos na linha quatro seriam executado — e nada mais. Windows PowerShell ainda não avalia a segunda expressão elseif na linha de cinco.

Page 11: Powers Hell

O caractere # é um caractere de comentário, tornando Windows PowerShell essencialmente ignorar qualquer coisa a partir daí até um carro retorno. Observe também o cuidado com os quais essas construções foram formatadas. Você também pode ver a formatação como esse de alguns caras:

Não importa onde você pode colocar as chaves. No entanto, o que faz a diferença é que você seja consistente na sua colocação para que seus scripts sejam mais fáceis de ler. Também é importante recuar, no mesmo nível exato, cada linha dentro de chaves.

O ISE de Windows PowerShell permite que você use a tecla Tab para essa finalidade e o padrão será um recuo de quatro caracteres. Recuo do seu código é uma prática recomendada de núcleo. Caso contrário, você terá um tempo difícil correspondência corretamente abrindo e fechando chaves em scripts complexos. Além disso, todas as outras crianças Windows PowerShell fará com que você diversão. Considere este script mal formatado:

function mine {if ($this -eq $that){get-service}}

Isso é muito mais difíceis de ler, depurar, solucionar problemas e manter. Enquanto o espaço após os parênteses de fechamento não é necessário, ele tornam mais fácil ler seu script. O código recuado não é necessário, mas ela facilita o script a seguir. Considere isso:

function mine { if ($this -eq $that){ get-service }}

Colocar um colchete de fechamento em uma linha por si só não é necessário para o shell, mas ele é apreciado pelo olho humano. Ser um formatador organizado, e você terá menos problemas em seus scripts.

A construção, enquanto

Esta é uma construção de loop em Windows PowerShell. Ele foi projetado para repetir um bloco de comandos desde que alguma condição for verdadeira, ou até que uma condição for verdadeira. Aqui está o uso básico:

Do { # commands} While ($this -eq $that)

Nesta variação da construção, os comandos dentro dos colchetes curvas sempre serão executado pelo menos uma vez. While condição não é avaliada até que a primeira

Page 12: Powers Hell

execução. Você pode mover While, caso em que os comandos serão executados somente se a condição for verdadeira em primeiro lugar:

While (Test-Path $path) { # commands}

O segundo exemplo de aviso não usa um operador de comparação, como - EQ. Isso ocorre porque o cmdlet Test-Path acontece retornar verdadeiro ou falso inicial. Não é necessário para comparar que como True ou False para que a expressão trabalhar.

A expressão entre parênteses, usada com essas construções scripts simplesmente precisa simplificar para True ou False. Se você estiver usando um comando como, por exemplo, Test-Path, que sempre retorna VERDADEIRO ou falso, que é tudo o que você precisa. Como sempre, há um tópico "sobre" no shell que demonstra a outras formas de usar essa construção.

A construção ForEach

Essa construção é semelhante em operação para o cmdlet ForEach-Object. Ele difere somente em sua sintaxe. A finalidade de ForEach é pegar uma matriz (ou coleção, que é o mesmo que uma matriz em Windows PowerShell) e enumerar os objetos no array, para que você possa trabalhar com um de cada vez:

$services = Get-ServiceForEach ($service in $services) { $service.Stop()}

É fácil para principiantes overthink essa construção. Tenha em mente que a palavra em inglês no plural "serviços" não significa nada para Windows PowerShell. Nome da variável é usada para lembrar-nos de que ele contém um ou mais serviços. Só porque ele é plural não faz o shell a se comportar de maneira especial.

A palavra-"in" chave na linha dois é parte da sintaxe ForEach. Variável $service é composto por. Isso poderia ter sido facilmente $fred ou $café e ele seriam funcionavam da mesma maneira.

Windows PowerShell se repetirá os comandos da construção — aqueles contidos entre chaves — uma vez para cada objeto na segunda variável (serviços de $). Cada vez, ele irá levar a um único objeto a partir da segunda variável (serviços de $) e colocá-lo na primeira variável (serviço$).

Dentro deste construtor, use a primeira variável (serviço$) para trabalhar com um objeto individual. Na linha de três, indica o período de "eu não quero trabalhar com o objeto inteiro, apenas um dos seus membros — o método Stop."

Há momentos em uso de ForEach é inevitável e desejáveis. No entanto, se você tiver um pouco de programação ou scripts de experiência, às vezes pode leap usando ForEach

Page 13: Powers Hell

quando não for a melhor abordagem. O exemplo anterior não é um bom motivo para usar ForEach. Isso seria mais fácil:

Get-Service | Stop-Service

O ponto aqui é avaliar o uso de ForEach. Certificar-se de que esta é a única maneira de realizar a tarefa à mão. Aqui estão algumas instâncias onde ForEach é provavelmente a única maneira de ir:

Quando você precisar executar um método contra um monte de objetos e houver um cmdlet que executa a ação equivalente.

Quando você possui um monte de objetos e precisa executar várias ações consecutivas em relação a cada um.

Quando você tem uma ação que só pode ser executada em relação a um objeto ao mesmo tempo, mas o script pode estar trabalhando com um ou mais objetos e você tem uma maneira de saber antecipadamente.

Outras construções

Windows PowerShell tem várias outras construções de scripts, incluindo o Switch, para, e assim por diante. Todos esses são documentados em "sobre" tópicos da Ajuda dentro do shell. Às vezes, você pode usar as construções abordadas aqui para substituir essas outras construções. Por exemplo, você pode substituir o Switch com uma instrução If construção que utiliza várias seções ElseIf. Você pode substituir para com ForEach, ou mesmo com o cmdlet ForEach-Object. Por exemplo, com um loop que será executado exatamente dez vezes:

1..10 | ForEach-Object -process { # code here will repeat 10 times # use $_ to access the current iteration # number}

Cabe a você selecionar a melhor construção para realizar o trabalho. Se você estiver navegando na Internet para scripts, esteja preparado para ser executado em qualquer e todas as variações.

Funções

Uma função é um tipo especial de construção usada para conter um grupo de comandos relacionados que realizam uma tarefa única e específica. Em termos gerais, você pode assumir qualquer script Windows PowerShell e "empacotá-lo" dentro de uma função:

function Mine { Get-Service Get-Process}Mine

Page 14: Powers Hell

Isso define uma nova função chamada "Meus". Que basicamente transforma as minhas em um comando, o que significa que você pode executar a função simplesmente inserindo seu nome. Isso é o que cinco faz de linha. Ele executa a função.

Normalmente, as funções estão contidas dentro de um arquivo de script. Um único script pode conter várias funções. Funções podem-se até mesmo conter outras funções.

No entanto, as funções são itens com escopo. Isso significa que você só pode usar uma função dentro do mesmo escopo no qual ele foi criado. Se você coloca uma função em um script e, em seguida, execute esse script, é possível que a função só estará disponível dentro do script e somente para a duração do script. Quando o script termina a execução, a função — como tudo no escopo do script — desaparecer. Aqui está um exemplo:

function One { function Two {Dir } Two}OneTwo

Suponha que você insira isso em um único arquivo de script e executar esse script. Linha sete executa a função a um, que começa na linha de um. A linha cinco executa uma função denominada dois, que começa na linha dois. Para que o resultado será uma listagem de diretório, que está na linha de três dentro da função dois.

Entretanto, a próxima linha a ser executada será a linha oito, e isso resultará em erro. O script não contém uma função chamada dois. Função dois é incluída dentro da função de um. Como resultado, que existe dentro da função de um escopo. Apenas outras coisas dentro de função, um podem ver dois. Tentando chamar dois a partir de outra anyplace resultará em erro.

Adicionando parâmetros a um Script

É raro para criar um script destinado a fazer exatamente a mesma coisa, sempre que ele for executado. Com mais freqüência, você terá de scripts que contêm algum tipo de dados da variável ou o comportamento da variável. Você pode acomodar essas variações com parâmetros.

Parâmetros são definidos de maneira especial na parte superior do script. Você pode preceder a essa definição com comentários, mas caso contrário, ele deve ser as primeiras linhas de executáveis do código dentro do script. Dentro da área de definição do parâmetro, cada parâmetro é separado do próximo por uma vírgula. Acompanhando a idéia de formatação organizada, ele ajuda a colocar cada parâmetro em uma linha própria. Veja um exemplo:

param ( [string]$computername, [string]$logfile,

Page 15: Powers Hell

[int]$attemptcount = 5)

Este exemplo define três parâmetros. Dentro do script, eles são simplesmente usados como qualquer outra variável. Você observará que na linha quatro, eu atribuído um valor padrão para o parâmetro de attemptcount $. O padrão será substituído por qualquer parâmetro de entrada, mas será usado se o script é executado sem que o parâmetro especificado.

Aqui estão várias maneiras em que o script pode ser executado, supondo que salvei como Test. ps1:

./test -computername SERVER./test -comp SERVER -log err.txt -attempt 2./test SERVER err.txt 2./test SERVER 2./test -log err.txt -attempt 2 -comp SERVER

O script aceita parâmetros, bem como qualquer cmdlet. Nomes de variáveis são usados como os nomes de parâmetro especificados com o traço usual que precede a todos os nomes de parâmetro na Windows PowerShell. Aqui está uma análise detalhada de como ele funciona:

Na linha um, eu estou apenas especificando um dos parâmetros —$ logfile, portanto, estará vazio e $attemptcount irá conter 5, o padrão.

Na linha dois, eu estou especificando três parâmetros, embora eu estou fazendo, de forma usando nomes de parâmetro de reduzido. Assim como acontece com os cmdlets, você só precisará digitar suficiente do nome do parâmetro Windows PowerShell saber qual delas você está falando.

A linha três me mostra novamente todos os três parâmetros, embora eu estou fazendo isso por posição, sem usar nomes de parâmetro. Desde que eu me lembro fornecer os valores na ordem exata em que os parâmetros estão listados no script, isso funcionará bem.

Linha quatro mostra o que acontece se você não for cuidadoso. Aqui, nome_do_computador $ conterá 'Servidor' e $logfile conterá 2, enquanto $attemptcount irá conter 5. É que provavelmente não pretendo. Quando você não usar nomes de parâmetro, é mais difícil ser flexível. Também é mais difícil para alguém para decodificar o que você entende, que torna mais difícil para que eles possam solucionar quaisquer problemas.

Linha de cinco é um exemplo melhor. Aqui, especifiquei parâmetros fora de ordem, mas é bem porque eu usado nomes de parâmetro. Como regra geral, eu sempre use nomes de parâmetro para o maior grau de flexibilidade. Eu não preciso lembrar-se a ordem na qual eles vieram.

Scripts avançados

Windows PowerShell oferece suporte a uma técnica para especificar informações adicionais sobre parâmetros. Isso permite que você declarar um parâmetro como

Page 16: Powers Hell

obrigatória, aceitar a entrada do pipeline e assim por diante. Essa técnica é chamada de ligação do Cmdlet.

Ele não altera o modo como o script usa parâmetros. Ele simplesmente apresenta o shell um pouco mais informações sobre os parâmetros. Você encontrará essa técnica mais comumente usada em uma função, mas a sintaxe é válida dentro de um script bem. Este é um exemplo simples:

[CmdletBinding()]param ( [Parameter(Mandatory=$True)] [string]$computername,

[Parameter(Mandatory=$True)] [string]$logfile,

[int]$attemptcount = 5)

Tudo o que eu adicionei era a instrução [CmdletBinding()], como a primeira linha executável do código dentro do script. Ele está correto para comentários deve preceder isso, mas nada mais. Também adicionei uma instrução de [Parameter()] a dois dos meus parâmetros. Dentro dessa instrução [Paramater()], indiquei que esses parâmetros são obrigatórios. Agora, se alguém tentar executar o script sem especificar esses parâmetros, Windows PowerShell irá solicitá-los para obter as informações.

Observe que o último parâmetro não tem quaisquer instruções especiais e todos os três parâmetros ainda aparecem em uma lista separada por vírgulas (o que significa que os dois primeiros parâmetros são seguidos por vírgulas). Há uma tonelada de outras instruções, você pode especificar para um parâmetro que você possa ler sobre o HYPERLINK "http://technet.microsoft.com/library/dd347600.aspx"about_functions_advanced_parameters tópico da Ajuda.

Essa foi uma revisão estonteante de alguns conceitos-chave de Windows PowerShell relacionados a scripts. Espero que você aprender algo novo. Ser capaz de criar scripts com parâmetros é especialmente útil, pois você poderá fazer scripts que pareçam e se comportam como cmdlets nativos do Windows PowerShell.

Amplie o Windows PowerShell com comandos personalizados

Como é provável que você já tenha descoberto, o Windows PowerShellTM é uma ferramenta eficiente e flexível. Mas talvez você ainda não saiba que é possível ampliar o Windows PowerShell escrevendo seus próprios cmdlets. Neste artigo, mostrarei como criar seus próprios cmdlets, escrevendo três cmdlets personalizados que lhe permitirão interagir com o IsolatedStorage.

Page 17: Powers Hell

Escolhi o IsolatedStorage para esses exemplos porque ainda não vi outros cmdlets relacionados ao IsolatedStorage e imaginei que sua funcionalidade seria útil, por oferecer um local para manter dados sem conflitos com outros aplicativos.Resumindo, o namespace System.IO.IsolatedStorage permite criar e utilizar armazenamentos isolados. Você pode ler e gravar dados que não seriam acessados por um código menos confiável, impedindo a exposição de informações confidenciais. Basicamente, os dados ficam disponíveis somente para o usuário atual ou para o assembly onde existe o código (podendo ser também isolados por domínio).Ao utilizar o IsolatedStorage nesses exemplos, salvarei um par de chaves/valores como cadeias de caracteres. O IsolatedStorage pode armazenar qualquer tipo de dados necessário; contudo, me limitarei às cadeias de caracteres para os fins deste artigo. Lembre-se de que o artigo trata, na verdade, de cmdlets. O IsolatedStorage serve apenas como exemplo de trabalho. Lembre-se também de que fornecerei apenas um ponto de partida aqui. Quando estiver pronto para aprofundar-se na criação dos seus cmdlets personalizados, consulte a SDK do Windows PowerShell.

Uma visão geral dos cmdletsQuando a Microsoft criou o Windows PowerShell, ele se destinava a facilitar a criação de outras ferramentas de linha de comando capazes de oferecer a mesma consistência e confiabilidade daquelas fornecidas como parte do Windows PowerShell. Isso é possível, em grande parte, porque o shell tem um único analisador para todos os cmdlets. Ou seja, esse modelo permite à equipe de desenvolvimento ter certeza de que todos os procedimentos de análise de argumentos, tratamento de erros e assim por diante serão realizados de forma semelhante para todas as ações que um usuário possa executar.Como resultado, há algumas diferenças bastante significativas entre um cmdlet do Windows PowerShell e os comandos de outros ambientes autônomos de shell. Por exemplo, um cmdlet é uma instância de uma classe do Microsoft® .NET Framework, e não um executável autônomo. Em geral, os cmdlets geram objetos como saída, em vez de texto, e não devem formatar sua saída. Um cmdlet processa seus objetos de entrada a partir de um pipeline de objetos, e não a partir de um fluxo de texto. Um cmdlet não deve analisar seus próprios argumentos nem especificar uma apresentação para os erros. Por fim, os cmdlets são orientados a registros e, em geral, processam um único objeto por vez.Os cmdlets têm uma estrutura específica; eles precisam ser atribuídos de determinada forma e têm de ser derivados de uma classe base específica. Se determinado cmdlet oferece suporte a parâmetros, esses parâmetros também precisam ser atribuídos de forma específica e o cmdlet tem de fornecer as implementações de alguns métodos específicos. Se você está se perguntando o que significa tudo isso, não se preocupe. Explicarei cada um desses requisitos em detalhes.

Atributo dos cmdletsPara declarar uma classe .NET como um cmdlet, é preciso atribuir a classe com o atributo CmdletAttribute (o único atributo obrigatório para qualquer cmdlet). Quando você especifica o atributo CmdletAttribute, precisa também especificar um par de termos constituído por um verbo e um substantivo, que será usado como nome do cmdlet. Esse nome deve descrever o que o cmdlet faz e com que tipo de recurso ele trabalha.Observe que o atributo CmdletAttribute em si é uma classe .NET. As propriedades da classe correspondem aos parâmetros disponíveis ao utilizar o cmdlet.

Page 18: Powers Hell

A parte do nome do cmdlet constituída por um substantivo permite diferenciar seu cmdlet personalizado de outros cmdlets. O substantivo especifica os recursos sobre os quais o cmdlet atua. O ideal é que o substantivo usado na nomeação do cmdlet seja bastante específico; caso tenha um termo genérico, você deve usá-lo como sufixo do cmdlet. Por exemplo, se você criar um cmdlet get-server para o seu banco de dados SQL, deve usar "New-SQLDatabase" em vez de "New-Database". A combinação de um verbo e um substantivo específicos torna mais fácil para o usuário detectar cmdlets com rapidez e prever sua funcionalidade. Lembre-se de que as palavras têm de ser facilmente reconhecidas. Então, você não deve utilizar pontuação reservada (barras, colchetes e assim por diante) nem caracteres curinga nos nomes dos seus cmdlets.Como estou criando cmdlets que funcionem com o IsolatedStorage do Windows®, tomarei isso como base para meu substantivo. Pode ser um pouco longo mas, quando se trata de nomes de cmdlets, quanto mais específico melhor.Há algumas diretrizes bastante objetivas quanto aos verbos que devem ser usados como nomes. Lembre-se de que a consistência é importante no Windows PowerShell. Por isso, use sempre os verbos adequados. Ao utilizar um dos verbos predefinidos para nomes, você aumenta a consistência entre os seus cmdlets personalizados, os incluídos e os criados por outros usuários. Por exemplo, para recuperar dados deve-se usar Get, em vez de Retrieve ou Acquire. Entre os verbos normalmente usados no Windows PowerShell estão: Add, Clear, Copy, Get, Join, Lock, Move, New, Remove, Rename, Select, Set, Split e Unlock. É possível saber qual o uso de cada cmdlet a partir do nome. Neste artigo, criarei três cmdlets: um para definir o conteúdo de dados do IsolatedStorage, um para recuperar o conteúdo e outro para remover o arquivo do IsolatedStorage. Assim sendo, criarei três cmdlets: Set-IsolatedStorageData, Get-IsolatedStorageData e Remove-IsolatedStorageFile.

Definição da classe dos cmdletsAgora, preciso criar o código que implementará meus cmdlets, começando por Set-IsolatedStorageData. Primeiro, declaro a classe do cmdlet:[Cmdlet(VerbsCommon.Set , "IsolatedStorage", SupportsShouldProcess=true)]

public class SetIsolatedStorageCommand : PSCmdlet

Observe que estou usando a combinação de maiúsculas e minúsculas de Pascal e que o nome da classe inclui o nome formado por verbo e substantivo do cmdlet. Isso não é estritamente necessário, mas facilita muito a leitura do código.A atribuição do cmdlet indica que estou usando um verbo comum, nesse caso Set. Caso seja possível, você deve sempre usar verbos comuns ao criar cmdlets. Definir SupportsShouldProcess como True indica que o cmdlet oferece suporte a chamadas para o método ShouldProcess, que permite ao cmdlet solicitar a verificação do usuário antes de realizar uma ação que modifique o sistema. Se esse atributo não estiver presente ou estiver definido como False (que é o valor padrão), indicará que o cmdlet não oferece suporte a chamadas para o método ShouldProcess.Todos os cmdlets que alteram recursos fora do Windows PowerShell devem ter a propriedade SupportsShouldProcess definida como true quando for declarado o atributo CmdletAttribute. Isso permitirá que o cmdlet chame o método ShouldProcess antes de executar sua ação. Se a chamada para ShouldProcess retornar false, a ação não será

Page 19: Powers Hell

executada (para obter mais informações sobre as solicitações de confirmação geradas pela chamada para ShouldProcess, consulte a documentação do MSDN® em msdn2.microsoft.com/ bb204629). Os parâmetros de cmdlet Confirm e WhatIf somente estão disponíveis para cmdlets que oferecem suporte a chamadas para ShouldProcess.A última parte declara a classe e usa PSCmdlet como a classe base. Isso significa que terei todos os comportamentos estendidos associados a um cmdlet do Windows PowerShell.O Windows PowerShell oferece suporte a cmdlets derivados de duas classes base diferentes: PSCmdlet e Cmdlet. Um cmdlet derivado de PSCmdlet fornece acesso ao runtime do Windows PowerShell. Isso habilita chamadas a outros scripts e permite acesso aos provedores do Windows PowerShell para trabalhar com o estado de sessão. PSCmdlet também fornece acesso aos recursos de login do Windows PowerShell, embora, em contrapartida, seja um pouco maior e crie uma dependência em relação ao runtime do Windows PowerShell.Os cmdlets derivados da classe Cmdlet geram o mínimo de dependências em relação ao runtime do Windows PowerShell. Isso traz alguns benefícios: esses cmdlets são um pouco menores, por terem menos funcionalidade, e há menor probabilidade de encontrar problemas devido a alterações no Windows PowerShell ao longo do tempo. Além disso, é fácil incluir esses cmdlets em outros aplicativos sem o runtime do Windows PowerShell.Se você planeja criar um cmdlet que sempre fará parte do ambiente do Windows PowerShell, deve usar PSCmdlet como classe base. No entanto, se acredita que seu código não será utilizado apenas no Windows PowerShell, deve usar Cmdlet como classe base.Nos meus exemplos, faço a derivação de PSCmdlet. Quando você criar seu próprio cmdlet, precisará fazer referência a System.Management.Automation.dll, que pode ser difícil de encontrar, pois está somente no GAC (cache de assembly global), mas logo em seguida ensinarei um truque para ajudá-lo com isso.

Definição de parâmetrosEm seguida, preciso avaliar os parâmetros a serem usados no meu cmdlet. Os parâmetros permitem ao usuário fornecer entradas ao cmdlet.Os nomes de parâmetros do cmdlet devem ser consistentes em todo o design do cmdlet. O SDK do Windows PowerShell traz sugestões detalhadas de nomes de parâmetros e de como usá-los em seu cmdlet para garantir a consistência com os outros cmdlets que você pode encontrar.Para declarar parâmetros para um cmdlet, é preciso primeiro definir as propriedades que representam os parâmetros. Para informar ao runtime do Windows PowerShell que uma propriedade é um parâmetro de cmdlet, é necessário adicionar um atributo ParameterAttribute à definição da propriedade.Os parâmetros têm de ser marcados explicitamente como públicos; os que não têm essa marcação tornam-se internos por padrão e não são encontrados pelo runtime do Windows PowerShell. Isso pode causar uma certa confusão quando você tentar descobrir por que seu cmdlet não tem os parâmetros que deveria ter.No meu exemplo, sei que todos os cmdlets precisarão de um nome que designe o nome real do arquivo do IsolatedStorage. Então, eis aqui a declaração de parâmetro — um parâmetro Name:private string _name = "PowerShellIsolatedStore";

/// <summary>name of store</summary>

Page 20: Powers Hell

[Parameter]public string Name{ get { return _name; } set { _name = value; }}

Quando você cria um parâmetro, deve escolher se ele será posicional ou nomeado. No caso de um parâmetro posicional, não é preciso fornecer o nome, apenas o valor:PS> cd c:\windows

Ao definir Position=num como parte do atributo, você designa a posição usada para o parâmetro. Se o parâmetro não for posicional, deixe de fora o atributo Position e use o nome do parâmetro na linha de comando para fornecer um valor.A documentação recomenda que, sempre que possível, você torne posicionais os parâmetros usados com freqüência. O único problema dessa diretriz é que, se você tiver vários parâmetros, talvez seja difícil lembrar-se de todos. Mas é claro que, mesmo quando um parâmetro é posicional, seu nome ainda pode ser usado na linha de comando.Os parâmetros de cmdlets podem ser definidos como obrigatórios, ou seja, precisam ter um valor atribuído antes que o runtime do Windows PowerShell invoque o cmdlet. A alternativa é defini-los como opcionais. Todos os parâmetros são definidos como opcionais por padrão. Então, para definir um parâmetro como opcional, basta omitir a propriedade Mandatory na declaração do atributo. Como armazenarei uma chave e um valor no armazenamento isolado, precisarei criar parâmetros para coletar esses valores (veja a Figura 1).  Figure 1 Parâmetros necessários para coletar chave e valor private string _key = null;[Parameter( Mandatory=true, Position=1, ValueFromPipelineByPropertyName=true )]public string Key{ get { return _key; } set { _key = value; }}

private string _value = null;

/// <summary>the value to store</summary> [Parameter( Mandatory=true, Position=2, ValueFromPipelineByPropertyName=true )]public string Value{ get { return _value; } set { _value = value; }}

Page 21: Powers Hell

Os parâmetros de cmdlets também podem ter aliases. Para informar ao Windows PowerShell que um parâmetro tem um alias, é necessário adicionar um atributo AliasAttribute à definição da propriedade. A sintaxe básica para declarar o atributo é [Alias("alias")]. No meu exemplo, eu crio um alias denominado Filename que se aplica ao parâmetro Name da seguinte forma:private string _name = "PowerShellIsolatedStore";/// <summary>name of store</summary>[Alias("Filename")][Parameter]public string Name{ get { return _name; } set { _name = value; }}

Parâmetros CommonO Windows PowerShell reserva alguns nomes de parâmetros, conhecidos como parâmetros Common, que você não pode utilizar: WhatIf, Confirm, Verbose, Debug, ErrorAction, ErrorVariable, OutVariable e OutBuffer. Além disso, os seguintes aliases para esses nomes de parâmetros são reservados: vb, db, ea, ev, ov e ob.

Parâmetros ShouldProcessOs parâmetros de outro grupo, o ShouldProcess, estão presentes somente quando o cmdlet especifica a palavra-chave SupportsShouldProcess em seu atributo CmdletAttribute.Quando o seu cmdlet oferece suporte a ShouldProcess, você tem acesso aos seguintes parâmetros em runtime: Confirm e WhatIf. Confirm especifica se a confirmação do usuário é necessária antes que um cmdlet execute uma ação que modifique o sistema. True indica que a confirmação é necessária; false indica que não.WhatIf especifica se um cmdlet deve informar ao usuário as alterações que seriam feitas se a ação fosse executada, sem que a ação tenha ocorrido. True indica que o usuário será informado sem que a ação tenha ocorrido e False indica que a ação precisa ocorrer.Quando SupportsShouldProcess for especificado, os cmdlets que tentarem declarar esses parâmetros falharão ao tentar registrar o cmdlet. Você não deve definir esses parâmetros diretamente no seu cmdlet. Em vez disso, inclua SupportsShouldProcess quando usar o atributo [Cmdlet(...)].

Conjuntos de parâmetrosO Windows PowerShell adota o conceito de conjuntos de parâmetros. Isso permite a você escrever um único cmdlet que exponha diferentes conjuntos de parâmetros ao usuário e retorne informações distintas com base nos parâmetros especificados pelo usuário. O cmdlet Get-EventLog (incorporado no Windows PowerShell), por exemplo, retorna informações distintas quando o usuário especifica os parâmetros List ou LogName. Quando LogName é especificado, o cmdlet retorna informações sobre os eventos em determinado log de eventos. Contudo, quando List é especificado, o cmdlet retorna informações sobre os arquivos de log propriamente ditos (e não as informações sobre eventos contidas neles). Nesse caso, List e LogName identificam dois conjuntos de parâmetros distintos.

Page 22: Powers Hell

Quando são definidos vários conjuntos de parâmetros, o cmdlet pode indicar qual o conjunto a ser utilizado se o Windows PowerShell não tiver informações suficientes para fazer essa determinação. O conjunto de parâmetros usado nesse caso é chamado de conjunto de parâmetros padrão e é especificado com o uso da palavra-chave DefaultParameterSet da declaração CmdletAttribute. Observe que não utilizo conjuntos de parâmetros nos meus cmdlets de exemplo.

Substituições de métodosA classe Cmdlet fornece métodos virtuais, mostrados na Figura 2, que podem ser usados para processar registros. É preciso substituir um ou mais desses métodos por todas as classes de cmdlets derivados.  Figure 2 Métodos virtuais

Método FinalidadeBeginProcessing Proporciona ao cmdlet uma funcionalidade opcional única de pré-

processamento.ProcessRecord Proporciona ao cmdlet uma funcionalidade de processamento registro-

por-registro. Pode ser chamado qualquer número de vezes ou não ser chamado, dependendo da entrada do cmdlet.

EndProcessing Proporciona ao cmdlet uma funcionalidade opcional única de pós-processamento.

StopProcessing Interrompe o processamento quando o usuário interrompe o cmdlet de forma assíncrona; por exemplo, digitando a combinação de teclas Ctrl+C.

Como os meus cmdlets lidam com arquivos, utilizarei o método BeginProcessing para implementar o código usado para abrir os arquivos do IsolatedStorage, conforme mostrado na Figura 3. Vale a pena observar alguns detalhes. Em primeiro lugar, estou abrindo os arquivos no modo de criação, o que representa uma alteração no sistema, então preciso encapsular o código em um bloco ShouldProcess. Isso significa que terei a oportunidade de avisar ao usuário sobre o que farei antes de realmente fazê-lo (isso ocorre quando o usuário utiliza os argumentos Confirm ou WhatIf). Observe também que vou capturar exceções e encapsulá-las com ThrowTerminatingError. Neste exemplo, se algo der errado, não terá prosseguimento, porque a abertura do arquivo terá falhado. Em geral, não convém capturar todas as exceções; mas, como vou encapsulá-las com ThrowTerminatingError, poderei fornecer algumas informações adicionais em caso de falha.  Figure 3 Usando o método BeginProcessing protected override void BeginProcessing(){ try { if ( ShouldProcess( Name )) { WriteVerbose("Opening Isolated Storage: " + Name); isoStore = this.GetMyStore(); fs = new IsolatedStorageFileStream( Name, FileMode.OpenOrCreate|FileMode.Append, FileAccess.Write, isoStore

Page 23: Powers Hell

); sw = new StreamWriter(fs); WriteDebug("Stream encoding: " + sw.Encoding); } }

catch ( Exception e )

{ this.closeStreams(); ThrowTerminatingError( new ErrorRecord( e, "OpenIsolatedStorage", ErrorCategory.NotSpecified, Name ) ); }}

Na minha implementação, utilizarei ProcessRecord para o trabalho de criar novas entradas de dados no armazenamento isolado (veja a Figura 4). Observe o uso das instruções try/catch, a fim de adicionar mais informações caso ocorra um erro. Neste caso, estou usando WriteError em vez de ThrowTerminatingError, pois não preciso interromper o pipeline no caso de uma falha de gravação.  Figure 4 Usado ProcessRecord em Set-IsolatedStorageData protected override void ProcessRecord(){ try { // Remember ShouldProcess may not have opened the file if(sw != null ) { WriteVerbose("Setting " + Key + " = " + Value); sw.WriteLine(Key + "=" + Value); } } catch ( Exception e ) { WriteError( new ErrorRecord( e, "SetIsolatedStorageValue", ErrorCategory.NotSpecified, Name ) ); }}

Como abri o armazenamento isolado a partir de BeginProcessing, usarei o método EndProcessing para fechar os arquivos. Não estou lendo ou gravando no arquivo do

Page 24: Powers Hell

armazenamento isolado em Remove-IsolatedStorageFile; portanto, usarei EndProcessing para remover o arquivo desse cmdlet. Eis aqui a aparência do código de EndProcessing nos cmdlets Get-IsolatedStorageData e Set-IsolatedStorageData:protected override void EndProcessing(){ if (sw != null ) { sw.Close(); } if (fs != null ) { fs.Close(); } if (isoStore != null ) { isoStore.Close(); }}

O código de Remove-IsolatedStorageFile é um pouco mais complicado. Nesse cmdlet, eu excluo o arquivo propriamente dito usando os métodos adequados a partir do objeto IsolatedStorage: if(ShouldProcess("Remove Isolated Storage")) { WriteVerbose("Deleting Isolated Storage: " + Name); isoStore = this.GetMyStore(); isoStore.DeleteFile(Name); }

Observe que utilizo ShouldProcess novamente. Como vou fazer uma alteração no sistema, preciso notificar o usuário sobre o que estou prestes a fazer, caso ele queira essa informação.

Emitindo resultadosO objetivo do Windows PowerShell é produzir resultados, mas é preciso encontrar um equilíbrio na maneira de fornecer esses resultados. Faz parte desse equilíbrio a certeza de retornar o máximo possível de informações sem causar muito impacto sobre o desempenho (como o uso excessivo de memória, o tempo de execução longo demais e assim por diante). Vou salvar texto em um arquivo e poderia, simplesmente, retornar apenas o texto em si. Embora isso fosse suficiente, eu gostaria de demonstrar algo melhor. Por isso, fornecerei a chave e o valor.Quando estava aprendendo a usar o IsolatedStorage, percebi que é difícil encontrar o arquivo efetivamente usado para o armazenamento. Então, quero incluir essa informação nos meus resultados. Isso os tornará mais úteis. Retornando a chave, o valor e o caminho para os dados, meu objeto terá a seguinte aparência:public class IsolatedStorageData{ public string Key; // The Key public string Value; // The Value public string FullName; // The path to the storage}

Também preciso avaliar que tipo de cadeia de caracteres o método ToString do objeto deve retornar. Por padrão, a maioria dos objetos .NET retorna apenas o nome do tipo, o que não é muito útil. Então, farei com que o método ToString retorne o Value, em vez do nome do tipo.Para obter o valor do nome de arquivo real do membro FullName, preciso fazer uma certa reflexão, porque essa informação não aparece como parte das informações sobre o

Page 25: Powers Hell

IsolatedStorage. Assim, a implementação do objeto que utilizarei para os resultados terá a aparência mostrada na Figura 5.  Figure 5 Objeto usado para os resultados public class IsolatedStorageData{ public string Key; // The Key public string Value; // The Value public string FullName; // The path to the storage public override string ToString() { return Value; } public IsolatedStorageData( string _key, string _value, IsolatedStorageFileStream _fs ) { Key = _key; Value = _value; FullName = _fs.GetType() . GetField("m_FullPath", BindingFlags.Instance|BindingFlags.NonPublic ) . GetValue(_fs).ToString(); }}

Mais uma vez, lembre-se de que, para proporcionar consistência entre todos os cmdlets, os cmdlets do Windows PowerShell retornam objetos em um pipeline, e não texto em um fluxo. WriteObject é usado pelo cmdlet para emitir os resultados. Após recuperar os dados no arquivo do IsolatedStorage, eu os converto em uma instância de um tipo IsolatedStorageData e uso WriteObject para emitir os resultados no pipeline.

Emitindo relatórios sobre condições de erroAo executar o código, haverá momentos em que nem tudo funcionará como você deseja e ocorrerá algum tipo de erro. O Windows PowerShell conta com alguns comportamentos relativamente sofisticados para essas circunstâncias, e seus recursos de emissão de relatórios sobre erros permitem um controle bastante refinado sobre os acontecimentos.Nem sempre a ocorrência de um erro é algo catastrófico. Por exemplo, se você quer remover milhares de arquivos em um diretório, a falha em remover um ou dois desses arquivos não invalida todas as demais exclusões. Os erros desse tipo são considerados não-fatais, ou seja, embora sejam erros, não farão com que você precise interromper o que estiver fazendo. Você pode continuar a remover os arquivos removíveis.No entanto, há operações das quais não é possível recuperar-se. Suponha que você precisa criar um arquivo temporário para manter alguns dados que usará mais tarde. Se não conseguir criar e usar o arquivo temporário, não haverá sentido em prosseguir com o restante da operação, pois os dados necessários não estarão disponíveis. Isso se qualifica como um TerminatingError. No Windows PowerShell, dois métodos de cmdlet diferentes — WriteError e ThrowTerminatingError — permitem fazer essa distinção.WriteError é usado sempre que ocorre alguma circunstância excepcional na execução do cmdlet, mas que não é fatal para a operação geral do cmdlet. O método adota uma instância

Page 26: Powers Hell

de um ErrorRecord como argumento, permitindo que você inclua mais do que apenas a exceção (a causa do erro).Você não deve lançar uma exceção em um cmdlet. Em vez disso, ThrowTerminatingError permite interromper a execução do pipeline e fornecer muito mais informações do que seria possível com uma exceção.Você perceberá no código dos meus cmdlets que uso ThrowTerminatingError em BeginProcessing, mas então uso WriteError nos métodos ProcessRecord. Isso ocorre porque, se eu não obtiver acesso ao IsolatedStorage, não serei capaz de fazer muito no que diz respeito à operação do cmdlet em geral. Contudo, se eu encontrar um erro na leitura do arquivo, talvez consiga recuperar-me e continuar.

Solucionando problemas em mensagensO Windows PowerShell oferece várias formas de comunicação com o usuário. Há três métodos que podem ser utilizados para notificar o usuário quando você deseja comunicar algo que não é resultado de um erro. Você deve saber que, quando utiliza esses métodos, não pode redirecionar as mensagens que serão enviadas, mas pode suprimi-las definindo algumas preferências no shell. Esses métodos se comunicam diretamente com o aplicativo host, nesse caso PowerShell.exe, escrevendo, por sua vez, na janela do console. As variáveis de preferências oferecem uma grande variedade de comportamentos, que vão desde não escrever nada até perguntar se a mensagem deve ser escrita antes de continuar.Eu uso esses métodos nos meus cmdlets de exemplo um pouco mais do que você provavelmente usará, pois quero mostrar como podem ser úteis. Se você tem um código complicado que precisa de algo mais do que apenas erros ou resultados, utilize os métodos disponíveis. A última coisa que você deseja é usar algo como System.Console.WriteLine em um cmdlet. Em primeiro lugar, por tratar-se de uma prática inadequada; em segundo lugar, porque você não deve depender do aplicativo host, já que talvez o console nem mesmo esteja presente.Você deve usar WriteVerbose caso tenha informações extras fora de banda que deseja transmitir ao usuário. Isso não se destina a mensagens do desenvolvedor; serve para permitir que o usuário saiba o que está acontecendo nos bastidores. A interface é muito simples. Eis aqui um exemplo do meu Remove-IsolatedStorageFile, que utiliza WriteVerbose para gerar uma saída informando que está prestes a remover o arquivo do IsolatedStorage:if(ShouldProcess("Remove Isolated Storage")){WriteVerbose("Deleting Isolated Storage: " + Name);isoStore = this.GetMyStore();isoStore.DeleteFile(Name);}

Use WriteDebug quando quiser comunicar-se com os desenvolvedores, dando-lhes a chance de solucionar problemas de comportamento errôneo no seu cmdlet. Normalmente, você anexaria um depurador ao sistema, mas nem sempre isso é possível em campo. WriteDebug ajudará nessas situações.WriteWarning é mais uma forma de comunicação com o usuário, fornecendo informações sobre a execução do seu cmdlet (não usei esse método nos meus cmdlets de exemplo). Um bom exemplo do uso de WriteWarning é quando você deseja alterar o comportamento de um cmdlet ao longo do tempo. Você pode usar WriteWarning se for descartar um

Page 27: Powers Hell

parâmetro e quiser informar ao usuário que pare de utilizá-lo e adote em seu lugar um novo parâmetro.

Grupos de cmdletsOs meus três cmdlets têm relação com o IsolatedStorage, portanto todos os substantivos têm a mesma raiz: IsolatedStorage. Dois cmdlets se destinam a trabalhar com os dados em si (Set-IsolatedStorageData e Get-IsolatedStorageData) e o outro, a remover o arquivo (Remove-IsolatedStorageFile).Como todos esses cmdlets têm um parâmetro em comum (o nome do arquivo que será efetivamente utilizado), não preciso implementar o mesmo parâmetro em todos os cmdlets. Em vez disso, posso criar uma classe base, IsolatedStorageBase, derivada de PSCmdlet, e os cmdlets serão derivados de IsolatedStorageBase (veja a Figura 6). Essa é uma boa forma de garantir a consistência entre os seus cmdlets.  Figure 6 Classe base IsolatedStorageBase public class IsolatedStorageBase : PSCmdlet{ [Parameter] public string Name ...}[Cmdlet(VerbsCommon.Set, "IsolatedStorageData",SupportsShouldProcess = true)]public class SetIsolatedStorageDataCommand: IsolatedStorageBase{...}[Cmdlet(VerbsCommon.Get, "IsolatedStorageData")]public class GetIsolatedStorageDataCommand: IsolatedStorageBase{...}[Cmdlet(VerbsCommon.Remove,"IsolatedStorageFile",SupportsShouldProcess = true)]public class RemoveIsolatedStorageFileCommand: IsolatedStorageBase{...}

Criando um snap-inPara usar esses novos cmdlets, você precisará adicioná-los ao ambiente do Windows PowerShell. O Windows PowerShell tem a capacidade de adicionar cmdlets dinamicamente à sessão por meio de um snap-in. Para evitar a possível confusão com os snap-ins do MMC, um snap-in do Windows PowerShell é chamado de PSSnapIn.Para criar um PSSnapIn, é preciso escrever um pouco de código para executar duas tarefas. Em primeiro lugar, fornecer identificação ao seu snap-in, permitindo distingui-lo de outros snap-ins instalados no sistema. Em segundo lugar, fornecer informações para a instalação adequada do snap-in e para a criação das entradas adequadas no Registro, permitindo ao Windows PowerShell localizar o assembly.

Page 28: Powers Hell

Há dois tipos de snap-ins do Windows PowerShell no namespace System.Management.Automation: PSSnapIn e CustomPSSnapIn. Use PSSnapIn quando quiser registrar automaticamente todos os cmdlets e provedores em um assembly. CustomPSSnapIn deve ser usado quando você quiser registrar um subconjunto dos cmdlets e provedores em um assembly, ou então quando quiser registrar cmdlets e provedores em diferentes assemblies. O código do meu snap-in é mostrado na Figura 7. É bem simples. Eu apenas substituo os membros adequados e já está praticamente pronto.  Figure 7 Snap-in para os meus cmdlets personalizados // This class defines the properties of a snapin[RunInstaller(true)]public class IsolatedStorageCmdlets : PSSnapIn{ /// <summary>Creates an instance of DemoSnapin class.</summary> public IsolatedStorageCmdlets() : base() { } ///<summary>The snap-in name that is used for registration</summary> public override string Name { get { return "IsolatedStorageCmdlets"; } } /// <summary>Gets vendor of the snap-in.</summary> public override string Vendor { get { return "James W. Truher"; } } /// <summary>Gets description of the snap-in. </summary> public override string Description { get { return "Isolated Storage Cmdlets"; } } /// <summary>The format file for the snap-in. </summary> private string[] _formats = { "IsolatedStorage.Format.ps1xml" }; public override string[] Formats { get { return _formats ; } }}

FormatandoObserve na Figura 7 que há um valor para o membro Formats do snap-in. Isso permite criar diretivas de formatação para os objetos emitidos pelos cmdlets. Neste caso, tenho um objeto simples, mas não quero apresentar todos os membros por padrão. Quero ter certeza de imprimir apenas as informações úteis, que neste caso são a chave e o valor.A criação desses arquivos de formato, por si só, já seria suficiente para produzir um artigo inteiro, e o assunto vai além do escopo deste artigo. A Figura 8 apresenta um código de exemplo que cria um formato de tabela com as duas colunas nas quais tenho interesse: a chave e o valor do objeto IsolatedStorageData. Para os fins deste exemplo, você pode simplesmente criar um arquivo denominado IsolatedStorage.Format.ps1xml no mesmo diretório do assembly e, quando o snap-in for carregado, não verá nenhum erro.

Page 29: Powers Hell

 Figure 8 Arquivo de formato de exemplo <Configuration> <ViewDefinitions> <View> <Name>IsolatedStorage</Name> <ViewSelectedBy> <TypeName>IsolatedStorageSnapin.IsolatedStorageData</TypeName> </ViewSelectedBy> <TableControl> <TableHeaders> <TableColumnHeader> <Label>Key</Label> <Width>12</Width> </TableColumnHeader> <TableColumnHeader /> </TableHeaders> <TableRowEntries> <TableRowEntry> <TableColumnItems> <TableColumnItem> <PropertyName>Key</PropertyName> </TableColumnItem> <TableColumnItem> <PropertyName>Value</PropertyName> </TableColumnItem> </TableColumnItems> </TableRowEntry> </TableRowEntries> </TableControl> </View> </ViewDefinitions></Configuration>

Instalando e carregando um PSSnapInÉ muito fácil instalar um snap-in. Basta executar Installutil.exe com o caminho do seu assembly. Quando esse utilitário é executado, cria algumas entradas no Registro em HKLM\SOFTWARE\Microsoft\PowerShell\1\PowerShellSnapins\<snapinname>. Quando o Windows PowerShell carrega um snap-in, essas entradas são usadas para carregar o assembly e localizar os diversos arquivos de configuração. Também vale a pena mencionar que Installutil.exe é o método de instalação recomendado somente durante o desenvolvimento, uma vez que as ações do utilitário não registram adequadamente suas dependências de desinstalação. Para a instalação em ambientes de produção, configure diretamente as entradas no Registro.Também é fácil carregar o snap-in. Os principais cmdlets que você utilizará são Add-PSSnapIn, Remove-PSSnapIn e Get-PSSnapIn. Como era de se esperar, add-PSSnapIn adiciona um ou mais snap-ins do Windows PowerShell à sessão atual. Remove-PSSnapIn remove snap-ins da sessão atual. E Get-PSSnapIn recupera os snap-ins do Windows PowerShell no computador.

Page 30: Powers Hell

Observe que, na verdade, Remove-PSSnapIn não descarrega o assembly. Ele apenas remove os cmdlets e provedores das listas que o Windows PowerShell utiliza para localizar cmdlets e provedores de acesso.

Juntando as peçasDepois de fazer tudo o que acabo de descrever, estou pronto para começar a usar meus novos cmdlets personalizados. Logicamente, estou fazendo tudo isso a partir da linha de comando. Primeiro, preciso compilar o código dos cmdlets e do snap-in. Lembra-se de que eu disse que precisaria compilar esse código com uma referência a System.Management.Automation.dll? Essa DLL pode ser encontrada no SDK e no GAC; mas, se você não tiver o SDK instalado, não se preocupe. Este pequeno script pode criar facilmente o assembly Snap-In. Primeiro, preciso criar um alias para o compilador C#. Quando estiver estabelecido, localizo System.Management.Automation.dll e faço a compilação do assembly:New-Alias csc "${V2Framework}\csc.exe"$SMADLL = [PSObject].Assembly.Locationcsc /target:library IsolatedStorageSnapin.cs /r:$SMADLL

Agora que já compilei o assembly, posso usar Installutil.exe para registrar meu novo snap-in, conforme mostrado na Figura 9. Quando a instalação for concluída com êxito, poderei localizar o novo PSSnapIn:

Figura 9 Registrando o snap-in (Clique na imagem para aumentar a exibição)PS> get-PSSnapIn -reg iso*

Name : IsolatedStorageCmdletsPSVersion : 1.0Description : Isolated Storage Cmdlets

Então, adiciono o snap-in à minha sessão:PS> add-PSSnapIn IsolatedStorageCmdlets

E agora posso usar os cmdlets, conforme mostrado na Figura 10!

Figura 10 Usando os cmdlets (Clique na imagem para aumentar a exibição)

Criando Grupos Locais Com PowershellNeste post vou mostrar (Novamente) um script Powershell para gerenciar grupos locais, batendo novamente na mesma tecla, sobre quão poderoso o Powershell  pode ser…Vamos ao script:$GrupoLocal = $null$ComputadorLocal = [adsi]“WinNT://localhost,computer”[array]$grupos_list = “grupo1″,”grupo2″,”grupo3″,”Administradores”foreach($grupo in $grupos_list){           if([ADSI]::Exists(“WinNT://localhost/$grupo,group”)){

Page 31: Powers Hell

           write-host “existe”           } else {          write-host “vai criar…”          $GrupoLocal = $ComputadorLocal.Create(“group”,$grupo)          $GrupoLocal.SetInfo()         }}

Agora as explicações, Iniciamos o script criando um objeto de conexão com o computador local, $ComputadorLocal = [adsi]“WinNT://localhost,computer”, onde passamos o nome do computador e o tipo de objeto a qual estamos nos conectando.

Após isso temos uma Array (simplificando para quem não entende: Um grupo de strings.) que tem o nome dos grupos que quero verificar se existem no meu computador, [array]$grupos_list = “grupo1″,”grupo2″,”grupo3″,”Administradores” ,  utilizamos essa Array para efetuar um foreach (“para cada”)  grupo na array,onde ele vai verificar se o grupo existe, caso o grupo exista if (se), ele não faz nada, caso o grupo não exista else (se não),  criamos um novo objeto que será o grupo utilizando a conexão que já havíamos estipulado antes.

Por fim efetuamos a criação do grupo utilizando o método SetInfo() no objeto que criamos para ser o grupo.

Powershell para desenvolvedores – Parte 1 – Conhecendo…

Olá pessoal, tudo certo?

Sempre senti falta de um bom Shell em ambientes Microsoft.

Quem usa Windows sempre careceu de uma alternativa interessante que pudesse ser, pelo menos minimamente, comparada com o Bash dos sistemas Unix-like. Ao meu ver, esta alternativa é o Powershell!

Há quem considere o Powershell demasiadamente vervboso. Há quem não goste do Powershell por sua “dependência” com  o .NET Framework. Eu gosto do Powershell por essas mesmas duas razões.

Nessa nova série pretendo demonstrar boas funcionalidades do Powershell para desenvolvedores. No post de hoje, mostro alguns conceitos fundamentais.

O que é o Powershell?Windows Powershell é a promessa (ainda?!) da Microsoft para revolucionar o mundo da gestão de sistemas por meio de shells baseados em linha de comando. Através de pipelines baseadas em objetos, do ponto de vista de administradores de sistema, Powershell constitui um enorme salto em produtividade e potencialidade.

Page 32: Powers Hell

Powershell é mais que uma versão aprimorada do Prompt de Comandos do Windows. Powershell é voltado a scripts e é parte integrande do Windows em versões posteriores ao 2008. Se o seu Windows não possui Powershell, baixe-o aqui.

Considerações importantes para o PowershellAlguns aspectos do Powershell são muito relevantes para quem está começando. Considere:

Powershell funciona muito bem com comandos e aplicações padrões do Windows. Isso significa que não é necessário descartar qualquer conhecimento que você já possua sobre como utilizar o Prompt de Comandos do Windows;

Powershell apresenta um tipo interiramente novo de comandos: os cmdlets. Esses comandos obedecem uma sintaxe padrão baseada em “Verbo-Substântivo” fácil de lembrar;

Powershell entende e consegue manipular objetos. Na prática, podemos instanciar e manipular objetos .NET diretamente no shell;

Powershell é auto-explicativo. Com apenas três comandos o usuário consegue obter informações sobre como utilizar e o funcionamento das funcionalidades que o Powershell oferece;

Powershell facilita a construção de scripts permitindo a automação de tarefas simples;

Powershell tem suporte nativo para muitas tecnologias. Com ele é fácil trabalhar com .NET, COM, WMI, XML, AD, …;

Powershell simplifica a utilização de depósitos de dados. Um mesmo modelo de trabalho é utilizado para manipular, por exemplo, o registro do windows e a estrutura de arquivos e pastas.

Iniciando o PowershellCarregar o Powershell é muito fácil. Na prática, basta iniciar o Powershell.exe no lugar de cmd.exe. Você também encontra o Powershell em “Iniciar – > Todos os Programas –> Acessórios –> Windows Powershell”.

 

Além dessa versão (simples), há um ambiente mais “carregado”: Windows Powershell ISE:

Pessoalmente, recomendaria começar com o prompt mais simples.

Primeiros passos (diretórios)Considere a seguinte listagem:

1 PS C:\Users\Elemar> pushd2 PS C:\Users\Elemar> cd \3 PS C:\> popd4 PS C:\Users\Elemar>

Page 33: Powers Hell

Já aqui, podemos começar a observar o “poder” do powershell. Observe:

Na linha 1, podemos observar um novo comando do Powershell: pushd (que é um alias para o comando Push-Location). O que ele faz? Salva o diretório atual em uma “pilha”.

Na linha 2, mudamos o diretório atual. Na linha 3, o comando que complementa pushd: popd (que é um alias para Pop-

Location). O que ele faz? Retira o diretório armazenado no topo da pilha tornando-o atual.

Para que serve? Bem, podemos mudar o diretório atual facilitando a utilização de comandos subsequentes, podendo voltar ao diretório que era atual no início das operações rapidamente e sem traumas.

Conhecendo os Cmdlets (comandos estruturados do Powershell)

Além dos aplicativos (executáveis) suportados nativamente, Powershell apresenta um novo tipo, mais poderoso, de comandos conhecidos comando cmdlet (pronuncia-se Command-Let).

Todos os cmdlets estão nomeados obedecendo ao padrão “Verbo-Substantivo”, como Get-Process, Get-Content,…, Stop-Process. Observe:

1 PS >Start-Process notepad 2 PS >Get-Process notepad 3 4 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName 5 ------- ------ ----- ----- ----- ------ -- ----------- 6 56 8 2512 7176 74 0,05 7888 notepad 7 56 7 2212 6812 73 0,11 10416 notepad 8 9 10 PS >

O que ocorreu? Na linha 1, iniciamos um processo (Sim, poderia ter digitado notepad diretamente). Na linha 2, obtive a lista de todos os processos com nome Notepad.

O padrão adotado pelo Powershell, embora verboso, simplifica a compreensão e o aprendizado. Uma vez que você se familiarize com o “trabalho” realizado por alguns verbos (como, por exemplo: Get, Set, Start e Stop), fica mais fácil entender como operar alguns novos “substantivos”.

Além disso, não é necessário lembrar, ou escrever, o “nome completo” de todos os Cmdlets. Podemos utilizar a tecla TAB para auto-completar os nomes dos comandos e de seus argumentos (Get-Pro<TAB> notepad !!)

Os mais conservadores podem dizer que, mesmo com o TAB, digita-se muito no Powershell. Para ajudar a melhorar a eficiência:

Powershell define aliases para todos os comandos mais comuns; Permite que o usuário defina os seus próprios aliases;

Page 34: Powers Hell

Totalmente case-insensitive. Isso significa que você pode usar Get-Process, get-process, GET-PROCESS (whatever) ;

Suporte nativo a objetosPowershell possui suporte rico a objetos. Observe:

1 PS >"Hello World"2 Hello World3 PS >"Hello World".Length4 115 PS >notepad6 PS >$notepad = Get-Process notepad7 PS >$notepad.kill()8 PS >

Comecemos pela linha 1. Basicamente, criamos um objeto “string”. Entendido? O bacana é que esse objeto string é o mesmo que você já conhece do .NET… Isso fica evidente nas linhas 3 e 4.

Na linha 5, abro um notepad. Na linha 6, recupero o processo com nome notepad (espero que haja somente um) e o armazeno em uma variável chamada $notepad (todas as variáveis em Powershell devem iniciar com  um $). Na linha 7 disparo uma chamada ao método Kill ($notepad tem um objeto do tipo Process) fechando o notepad.

Uma caluladora interativaO Powershell é uma calculadora nativa. Observe:

1 PS >2+22 43 PS >1mb4 10485765 PS >6gb/650mb6 9,452307692307697 PS >

Podemos realizar operações simples (como na linha 1). Quantos bytes há em um MB? Resposta solicitada e obtida nas linhas 3 e 4. Quandos CDs preciso para guardar 6 gigabytes? Respostas nas linhas 5 e 6.

1 PS >[DateTime]::Now 2 3 segunda-feira, 31 de janeiro de 2011 19:28:01 4 5 6 PS >[DateTime] "09/06/1979" 7 8 quinta-feira, 6 de setembro de 1979 00:00:00 9 10 11 PS >$howOld = [DateTime]::Now - [DateTime]"06/09/1979"12 PS >$howOld13

Page 35: Powers Hell

14 15 Days : 1155916 Hours : 1917 Minutes : 2918 Seconds : 5419 Milliseconds : 94920 Ticks : 998749794949064021 TotalDays : 11559,604108206822 TotalHours : 277430,49859696223 TotalMinutes : 16645829,915817724 TotalSeconds : 998749794,94906425 TotalMilliseconds : 998749794949,06426 27 28 PS >$howOld.Days29 1155930 PS >

Mais funcionalidades? Observe que consigo “acessar” um tipo do .NET colocando seu nome entre colchetes. Para acessar um membro estático, utilizo a notação com dois “dois pontos” (linha 1). Que dia é hoje? Linha 1 pergunta e linha 3 responde.

Powershell também oferece Cast simples. Que dia da semana eu nasci? Linha 6 pergunta, linha 8 responde.

Que idade eu tenho? Linha 11 pergunta, linhas 12 a 25 respondem. Observe na linha 28 que acessar propriedades do objeto é muito simples.

Combinando ComandosSempre que um comando gerar uma saída, podemos usar um pipe para passar esse resultado como entrada para outro comando. Se o segundo comando entender os objetos produzidos pelo primeiro comando, podera operar com os resultados.

Considere o seguinte comando (para fins de clareza, em várias linhas):

1 PS >Get-Process | 2 Sort-Object -desc WorkingSet | 3 Select-Object -First 10 | 4 Format-Table ProcessName, WorkingSet -auto

O cmdlet da linha 1 carrega a lista de todos os processos executando na máquina. Observe que a saída é uma coleção de objetos “Process” (que herdam de object), logo, são acessíveis para um comando que espera “objetos”.

Na linha 2, utilizo um cmdlet para ordenação. Por causa da pipe, esse cmdlet recebe a lista completa de objetos Process. Especifico que desejo que a saída seja esses objetos em sequência ordenada descendente pelo tamanho do workingset.

Na linha 3, utilizo um cmdlet para seleção. Por causa da pipe, esse cmdlet recebe uma lista de objetos Process ordenada. Especifico que desejo apenas os 10 primeiros elementos.

Page 36: Powers Hell

Na linha 4, utilizo um cmdlet para apresentação. Por causa da pipe, esse cmdlet recebe a lista com os 10 processes com maior workingset. Faço com que a saída seja uma tabela apenas com o nome do processo e o WorkingSet.

Obtendo ajuda em 3 comandos: Aprendendo CmdletsNa hora de “trabalhar” com uma nova tecnologia é que a “coisa pega”. Por sorte, Powershell ajuda a entender o sistema. Considere:

1 PS >Get-Command Format-*2 3 CommandType Name4 ----------- ----5 Cmdlet Format-Custom6 Cmdlet Format-List7 Cmdlet Format-Table8 Cmdlet Format-Wide

Na linha 1, solicitei a lista de comandos com nomes compatíveis com a máscara especificada.

Para saber detalhes de funcionamento de um comando, utilizamos o cmdlet Get-Help. Observe:

1 PS >Get-Help Format-Table2 3 NOME4 Format-Table5 6 SINOPSE7 Formata a saída como uma tabela.8 9 .. (continua)

Se temos um objeto, e desejamos saber o que podemos fazer com ele, usamos Get-Member. Observe:

1 PS >$notepad = Get-Process notepad 2 PS >$notepad | Get-Member 3 4 5 TypeName: System.Diagnostics.Process 6 7 Name MemberType Definition 8 ---- ---------- ---------- 9 Handles AliasProperty Handles = Handlecount10 Name AliasProperty Name = ProcessName11 NPM AliasProperty NPM = NonpagedSystemMemorySize12 PM AliasProperty PM = PagedMemorySize13 VM AliasProperty VM = VirtualMemorySize14 WS AliasProperty WS = WorkingSet15 .. (continua)

Page 37: Powers Hell

Scripting…Powerhell trata comandos digitados diretamente pelo usuário da mesma forma que os trataria em scripts. Observe:

1 PS >$ws = 02 PS >foreach ($process in Get-Process) {$ws+=$process.WorkingSet}3 PS >$ws4 32226222085 PS >

Crio uma variável (chamada $ws) para acomodar o total de a soma dos workingsets de todos os processos. Utilizo o comando foreach (na linha 2) para percorrer todos os processos abertos somando seus workingsets em $ws.

Outro bom exemplo:

1 PS >$wc = New-Object System.Net.WebClient2 PS >$feed = $wc.DownloadString("http://elemarjr.net/rss")3 PS >$feed4 <?xml version="1.0" encoding="UTF-8"?><rss version="0.92">5 <channel>6 <title>Elemar DEV</title>7 <link>http://elemarjr.net</link>8 <description>Tecnologia e desenvolvimento .net</description>9 ... (continua)

Na linha 1, crio um WebClient, depois, faço o download do RSS do meu blog. Observe que faço tudo isso de forma interativa. Agora, observe:

1 PS >Clear-History2 PS >$wc = New-Object System.Net.WebClient3 PS >$feed = $wc.DownloadString("http://elemarjr.net/rss")4 PS >Get-History | ForEach-Object {$_.CommandLine} > c:\temp\getfeed.ps15 PS >notepad c:\temp\getfeed.ps16 PS >c:\temp\getfeed.ps17 PS >

Começo limpando o histórico de comandos (linha 1). Depois, executo, de forma interativa, os comandos que desejo incluir em meu script. Por fim (linha 4), salvo a sequência de comandos em um arquivo de scripts (geralmente extensão .ps1). Na linha 5, abro o notepad para fazer eventuais retoques (como retirar o cmdlet Clear-History da primeira linha).

Suporte nativo a tipos especiaisComo já foi dito, Powershell oferece suporte nativo a diversas tecnologias. Entre elas, XML. Observe:

1 PS >$wc = New-Object System.Net.WebClient 2 PS >$content = $wc.DownloadString("http://elemarjr.net/rss") 3 PS >$xml = [xml]$content 4 PS >$xml 5 6 xml rss 7 --- ---

Page 38: Powers Hell

8 version="1.0" encoding="UTF-8" rss 9 10 PS >$xml.rss.channel.item | select Title11 12 title13 -----14 MSBuild 101 - Parte 4 - Simple Conditions15 Escrevendo um Engine para Xadrez - Parte 11 - Mais Bitboards, Xeques e Escapadas16 MSBuild 101 - Parte 3 -Items17 Escrevendo um Engine para Xadrez - Parte 10 - PieceSet, Side, AttackMoves, Rays e18 MSBuild 101 - Parte 2 - Conhecendo Tasks, Targets e Properties19 Escrevendo um Engine para Xadrez - Parte 9 - Refactoring e Redesign20 Sudoku - Backtracking e Pruning21 Feedback para Desenvolvedores22 Escrevendo um Engine para Xadrez - Parte 8 - Bispo e Dama23 Escrevendo um Engine para Xadrez - Parte 7 - O movimento da torre24 25 26 PS >

Na linha 3, declaro para o Powershell que o conteúdo da variável $content é xml. Depois disso, cada nodo é tratado pelo powershell como uma propriedade. Veja como, no exemplo, recupero os títulos de meus posts providos por RSS de maneira fácil e transparente.

Outro exemplo bacana, é a navegação pelo registro. Observe:

1 PS >Set-Location HKLM:\Software 2 PS >Get-ChildItem 3 4 Hive: HKEY_LOCAL_MACHINE\Software 5 6 SKC VC Name Property 7 --- -- ---- -------- 8 2 0 Alps {} 9 1 0 Apple Computer, Inc. {}10 1 0 ATI Technologies {}11 1 0 Axalto {}12 2 0 BioAPI {}13 0 1 BROADCOM {HostStoragePath}14 312 0 Classes {}15 8 0 Clients {}16 2 0 Creative Tech {}17 1 0 Cyberlink {}18 1 0 Debug {}19 1 0 Dell {}20 1 0 Dell Computer Corporation {}21 1 0 DeviceVM {}22 1 0 GEAR Software {}23 7 0 IDT {}24 1 0 IM Providers {}25 1 0 InstalledOptions {}26 8 2 Intel {LogElapsedTime, GUISettings}27 4 0 JavaSoft {}28 192 1 Microsoft {(default)}

Page 39: Powers Hell

Os cmdlets Set-Location e Get-ChildItem são usados, geralmente, para tratar do filesystem. Aqui, você pode ver esses mesmos comandos “navegando” pelo registro do Windows. Observe os aliases para esses comandos em ação:

1 PS >cd HKLM:\Software2 PS >dir

Powershell para desenvolvedores – Parte 2 – Operadores e funções

Olá pessoal, tudo certo?

No primeiro post dessa série, mostrei os elementos fundamentais para utilização do Powershell. No post de hoje, pretendo mostrar elementos que agradam todo programador: Operadores e funções.

Sem delongas…

Operadores de comparaçãoPowershell oferece um conjunto rico de operadores de comparação. Pela natureza “linha de comando”, não encontramos os opradores em sua sintaxe usual, mas não há nada de realmente desafiador aqui.

Os operadores de comparação ficaram assim:

-eq: igual a; -ne: diferente de; -ge: maior ou igual a; -gt: maior que; -le: menor ou igual a; -lt: menor que; -like -notlike -match – Combina com expressão regular; -notmatch – Não combina com expressão regular; -contains -notcontains -is -isnot

Comecemos por um exemplo:

1 PS > (dir).Count 2 25 3 PS > (dir).Count -eq 25 4 True 5 PS > (dir).Count -ne 25 6 False

Page 40: Powers Hell

7 PS > (dir).Count -gt 10 8 True 9 PS > (Get-Process).Count10 14211 PS > 1gb -eq 1mb * 102412 True

Nas linhas 1 e 2, evidencio que todo cmdlet que retorne uma “lista” de objetos é somente isso: uma lista. Quantos arquivos tenho em meu diretório atual? Pergunto na linha 1, respondo na linha 2. Quantos processos abertos? Linha 9 pergunta, linha 10 responde. Um gigabyte = 1024 mb? Conforme as linhas 11 e 12, sim!

Nas linhas entre 3 e 8, realizo algumas comparações simples. Outro exemplo:

1 PS > $result = .\curl http://elemarjr.net/feed/2 % Total % Received % Xferd Average Speed Time Time Time Current3 Dload Upload Total Spent Left Speed4 100 577k 0 577k 0 0 221k 0 --:--:-- 0:00:02 --:--:-- 237k5 PS > $xml = [xml] $result6 PS > ($xml.rss.channel.item).Count -gt 57 True

Logo na linha 1, uma operação bacana. Pego o resultado da operação de um aplicativo externo, o cURL (tema suficiente para um post inteiro), que é um “navegador web texto”, e atributo a variável $result.

Na linha 5, utilizo o suporte nativo do Powershell para xml. Na linha 6, verifico se a quantidade de nodos Item, em rss.channel, é maior que 10.

Agora, um pouco de expressões regulares:

1 PS > $h = "Hello World" 2 PS > $h -match "H.*World" 3 True 4 PS > $h -match "Hello" 5 True 6 PS > $h -match "Hello$" 7 False 8 PS > $h -match "^Hello" 9 True10 PS > $h -match "^ello"11 False

Aqui, temos algumas “comparações” realizadas com expressões regulares. Agora, considere o exemplo abaixo:

1 PS > "hello world" -eq "Hello World"2 True3 PS > "hello world" -ceq "Hello World"4 False

Page 41: Powers Hell

Pelas linha 1 e 2, percebemos que todos os operadores de comparação padrão do Powershell são case-insensitive. Para comparações case-sensitive, basta adicionar um c antes do nome do operador (linhas 3 e 4).

Agora, uma idéia dos operadores de lista:

1 PS > 1..10 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 810 911 1012 PS > 1..10 -contains 313 True14 PS > 1..10 -contains 915 True16 PS > 1..10 -contains 1117 False

 

Compreendido? 1..10 gera uma lista com números de 1 até 10. O –contains verifica se um elemento está na lista.

Aliás, aproveitando a brincadeira, observe:

1 PS > foreach ($n in 1..10) {$n*$n} 2 1 3 4 4 9 5 16 6 25 7 36 8 49 9 6410 8111 10012 PS >

Operadores de comparaçãoOs operadores lógicos são:

-and -or -xor: ou exclusivo (True –xor False = True; False –xor True = True, falso para todos

os outros casos. -not

Observe:

Page 42: Powers Hell

1 PS > (2 -eq 2) -and (4 -gt 2)2 True3 PS > -not (2 -eq 2)4 False5 PS >

Auto-explicativo, não é?!

Where-Object: o rei dos filtrosAgora que já sabemos fazer comparações, vejamos como construir filtros para nossas listas baseados em condições. Observe:

1 PS > Get-Process | Where-Object { $_.ProcessName -match "^W.*"} 2 3 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName 4 ------- ------ ----- ----- ----- ------ -- ----------- 5 152 13 30280 7908 103 0,09 6416 WebcamDell2 6 1128 160 151540 148552 463 116,69 9080 WindowsLiveWriter 7 81 10 1744 4664 47 0,08 528 wininit 8 132 10 3512 7824 53 0,27 756 winlogon 9 355 23 7188 15276 85 0,22 1708 wlanext10 345 21 6808 15232 77 0,19 3500 WLIDSVC11 59 6 1556 3608 33 0,00 3632 WLIDSVCM12 442 34 33316 41760 175 9,03 1372 WmiPrvSE13 355 15 12148 17152 61 9,13 3052 WmiPrvSE14 124 12 5760 7704 38 0,45 4984 WmiPrvSE15 142 12 4364 8172 36 0,66 5808 WmiPrvSE16 593 48 42032 57860 208 79,25 8544 wmplayer17 93 9 2228 6708 69 0,03 7048 wuauclt18 200 10 1844 5132 34 0,03 1500 WUDFHost19 20 21 PS > Get-Process | Where-Object 22 { $_.ProcessName -match "^W.*" -and $_.Handles -lt 100}23 24 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName25 ------- ------ ----- ----- ----- ------ -- -----------26 81 10 1744 4664 47 0,08 528 wininit27 59 6 1556 3608 33 0,00 3632 WLIDSVCM28 93 9 2228 6708 69 0,03 7048 wuauclt

Importante: O conteúdo das linhas 21 e 22 deve estar na mesma linha. Utilizei duas para respeitar o layout do blog.

O cmdlet WhereObject espera uma “expressão” lógica (que retorne verdadeiro ou falso). O elemento da lista que está sendo avaliado é representado pela variável reservada $_.

Refactoring no Powershell – Extract method (!?)Powershell permite que definamos funções. Criando algumas funções utilitárias, podemos simplificar nossos scripts. Observe:

1 PS C:\Users\Elemar> function EhPar($n) {$n % 2 -eq 0}

Page 43: Powers Hell

2 PS C:\Users\Elemar> EhPar(10)3 True4 PS C:\Users\Elemar> EhPar(5)5 False

Powershell para desenvolvedores – Parte 3 – Desvios condicionais e Loops

Olá pessoal, tudo certo?!

Passado algum tempo, voltamos ao Powershell. No primeiro post, apresentei os “primeiros passoas” para utilização dessa ferramenta. Na segundo post, falei sobre operadores e funções. Hoje, vou tratar de desvios condicionais e laços de petição.

Sem mais delongas…

Escrevendo scripts para PowershellAté aqui, todos os exemplos que foram apresentados eram comandos digitados “um-por-um” no console. Na maioria dos casos é suficiente, em outros tantos não é.

Scripts powershell são salvos em arquivos comuns com extensão .ps1. Você pode criar scripts utilizando o Notepad, se preferir. Eu crio meus scripts usando o PowerShell ISE (que acompanha o Powershell 2). Veja o programa na figura:

Como você pode ver, essa janela é dividida em três painéis:

1. painel de scripts – onde você cria seus arquivos de script; 2. painel de comando (no meio) – onde você pode digitar comandos powershell

isoladamente, da mesma forma que faria no console; 3. painel de saída (o primeiro, de baixo para cima) – onde são exibidos os resultados

de execução dos comandos ou scripts.

Bacana!

Repare que o painel para scripts do powershell suporta “code-completing” e “color-coding”. Além disso, na toolbar você pode perceber algumas ferramentas para apoiar a execução e depuração de scripts. Observe:

 

Desvios condicionais simples – if .. elseif .. elseEm powershell, podemos usar desvios condicionais elaborados, semelhantes aos disponíveis em nossas linguagens de programação. Observe:

Page 44: Powers Hell

01 if ($v -lt 5)02 {03     "Menor que 5."04 }05 elseif ($v -lt 15)06 {07     "Menor que 15."08 }09 elseif ($v -lt 40)10 {11     "Menor que 40."12 }13 else14 {15     "Whatever!"16 }

O conceito aqui é simples. Não vou explicar como funciona um if, já que essa série é direcionada para desenvolvedores. A única diferença percebida (mesmo) são os operadores de comparação.

Considere esse exemplo um pouco mais elaborado:

1 if(Get-Process | Where-Object { $_.Name -eq "calc" }) 2 { 3     "Windows calc is Running"4 } 5 else 6 { 7     "Windows calc is Not Running"8 }

Esse pequeno script verifica se a calculadora está em execução. Perceba que todo esse script poderia ser digitado em uma única linha dispensando a criação de um arquivo de script.

Só para lembrar: Para executar um arquivo de script (no meu exemplo, if.ps1) é necessário fazer referência ao diretório em que esse arquivo está salvo, mesmo que seja corrente (ex: “./if.ps1”).

Desvios condicionais com múltipla escolha – SwitchPowershell também fornece suporte a desvios condicionais usando switch. Observe:

Page 45: Powers Hell

1 switch ($v)2 {3     {$v -lt 5} { "Menor que 5."; break }4     {$v -lt 15} { "Menor que 15."; break }5     {$v -lt 40} { "Menor que 40."; break }6     40 { "40!"; break }7     default { "Whatever" }8 }

Como você pode observar, cada “opção de escolha” consiste de dois blocos.

Repetindo blocos de execução – forO que você acha de usar o bom e velho for do C# no Powershell? Você pode! Observe:

1 for ($counter = 1; $counter -le 10; $counter ++)2 {3     "Loop number $counter"4 }

Veja como é simples. A sintaxe é bem parecida com a que estamos acostumados no C#. Não acha?

Pecorrendo listas – foreach e ForEach-ObejctSe há for, deve haver foreach. Certo? Certo! Observe:

1 foreach ($file in dir)2 {3     "File length: " + $file.Length4 }

Como você já deve saber, dir é o alias para Get-ChildItem. Há um cmdlet com a mesma função de for, trata-se do ForEach-Object. Observe:

1 dir | ForEach-Object { "File length: " + $_.Length }

Repetindo … Repetindo .. – while / do..while / do..untilOutra semelhanca: Powershell suporta while. Observe:

1 $response = ""

Page 46: Powers Hell

2 while ($response -ne "q")3 {4     $response = Read-Host "Type something (q to quit)"5 }

Também tem do..while. Observe:

1 $response = ""2 while ($response -ne "q")3 {4     $response = Read-Host "Type something (q to quit)"5 }

E vai além: temos o do..until. Observe:

1 do2 {3     $response = Read-Host "Type something (q to quit)"4 } until ($response -eq "q")