Windows PowerShellCriando scripts uma linha por vez

Don Jones

Nas colunas anteriores, eu analisei o fato de o Windows PowerShell ser um shell. Ele é indicado para ser usado interativamente – em nada diferente do shell cmd.exe (ou prompt de comando) com que você provavelmente já está familiarizado. Ainda assim, o Windows PowerShell oferece suporte a uma linguagem de script – uma mais robusta que a linguagem em lotes do cmd.exe. E ela

é tão potente quanto, se não mais potente que linguagens como o VBScript. Entretanto, o fato de o Windows PowerShell™ ser um shell interativo torna o script consideravelmente fácil de se aprender. Na verdade, você pode desenvolver os scripts interativamente no shell, tornando possível escrever scripts uma linha por vez e observando imediatamente os resultados de seus esforços.

Essa técnica de criação de scripts interativa torna também a depuração mais fácil. Como você vê os resultados do seu script imediatamente, poderá rapidamente revisá-lo quando as coisas não forem como o esperado.

Nesta coluna, eu o orientarei em um exemplo de script interativo do Windows PowerShell. Criarei um script que leia os nomes de serviço de um arquivo de texto e configure cada modo de inicialização do serviço como Desabilitado.

O que desejo que você retire dessa orientação é o conceito de criar scripts do WindowsPowerShell em pequenas partes, em vez de tentar atacar um script inteiro em um grande bloco. Você pode pegar qualquer tarefa administrativa que precisa realizar e dividi-la em partes do componente e, em seguida, descobrir como fazer com que cada parte funcione independentemente.O Windows PowerShell fornece maneiras excelentes de unir essas partes, como você verá. E você descobrirá que, trabalhando em pequenas partes por vez, você terá mais conforto no desenvolvimento do script final.

Lendo nomes de um arquivo

Descobrir como ler arquivos de texto no Windows PowerShell pode ser frustrante. Se eu executar o *arquivo* Ajuda, tudo o que obtenho é ajuda para o cmdlet Out-File, que envia o texto a um arquivo em vez de ler dele. Sem ajuda nenhuma! Entretanto, os nomes de cmdlet do Windows PowerShell seguem um tipo de lógica que eu posso usar a meu favor. Quando o Windows PowerShell recupera alguma coisa, o nome do cmdlet normalmente começa com Get. Então executo o Help Get* para exibir esses cmdlets e, em seguida, rolo pela lista e visualizo o Get-Content. Parece promissor! Então executo o Help Get-Content para ver mais informações sobre o que ele faz (consulte a Figura 1) e parece que terei minhas necessidades atendidas.

Figura 1 Executando o Help Get-Content para obter mais informações

Figura 1** Executando o Help Get-Content para obter mais informações **(Clique na imagem para aumentar a exibição)

O Windows PowerShell trata praticamente tudo como objeto e os arquivos de texto não são exceção. Um arquivo de texto é tecnicamente uma coleção de linhas em que cada linha do arquivo funciona como uma espécie de objeto independente. Assim, se eu criei um arquivo de texto chamado C:\services.txt e preenchido o arquivo com nomes de serviço (colocando cada nome em sua própria linha dentro do arquivo), o Windows PowerShell pode ler os nomes individualmente, usando o cmdlet Get-Content. Como a idéia desse passo a passo é mostrar como os scripts podem ser desenvolvidos interativamente, começarei executando apenas o Get-Content, dando a ele o nome do meu arquivo de texto e observando o que acontece:

PS C:\> get-content c:\services.txt
messenger
alerter
PS C:\>

Como esperado, o Windows PowerShell lê o arquivo e exibe o nome. É claro, exibir simplesmente os nomes não é realmente o que desejo, mas agora eu sei que o Get-Content funciona da maneira como eu esperava que funcionasse.

Alterando um serviço

A próxima coisa que eu quero fazer é alterar o modo de inicialização do serviço. Mais uma vez, começo tentando encontrar o cmdlet correto. Então executo o Help *Service*. Isso retorna uma lista pequena e o cmdlet Set-Service parece ser o único que se ajusta às minhas necessidades. Desejo testar este cmdlet para me certificar de ter entendido como ele funciona antes de tentar incorporá-lo em um script. Executar o Help Set-Service mostra como o cmdlet deve funcionar, e um teste rápido confirma isso:

PS C:\> set-service messenger -startuptype
    disabled
PS C:\>

Combinando as partes

Agora preciso combinar a habilidade de ler nomes de serviço de um arquivo com o cmdlet Set-Service e é aí que os recursos eficientes de pipeline do Windows PowerShell entram em ação. Com o pipeline, a saída de um cmdlet pode ser passada como a entrada para um segundo cmdlet. O pipeline passa objetos inteiros. Caso uma coleção de objetos seja colocada no pipeline, cada objeto é passado pelo pipeline individualmente. Isso significa que a saída do Get-Content – que, lembre-se, é uma coleção de objetos – pode ser enviada por pipe para o Set-Service. Como o Get-Content está passando uma coleção, cada objeto ou linha do texto na coleção é enviada por pipe para o Set-Service individualmente. O resultado é que o Set-Service é executado uma vez para cada linha do meu arquivo de texto. O comando se parece com o seguinte:

PS C:\> get-content c:\services.txt | 
 set-service -startuptype disabled
PS C:\>

Aqui está o que acontece:

  1. O cmdlet Get-Content é executado e lê o arquivo inteiro. Cada linha do arquivo é tratada como um objeto exclusivo e, juntas, são uma coleção de objetos.
  2. A coleção de objetos é enviada por pipe para o Set-Service.
  3. O pipeline executa o cmdlet Set-Service uma vez para cada objeto de entrada. Para cada execução, o objeto de entrada é passado para o Set-Service como o primeiro parâmetro do cmdlet, que é o nome do serviço.
  4. O Set-Service é executado, usando o objeto de entrada do primeiro parâmetro e todos os demais parâmetros sejam especificados, o parâmetro -statuptype, neste caso.

O mais interessante é que, na verdade, eu realizei minha tarefa nesse ponto - e eu nem sequer escrevi um script ainda. Essa mesma ação seria difícil de se obter no shell do Cmd.exe e ela tomaria uma dúzia de linhas de código no VBScript. Mas o Windows PowerShell cuida de tudo isso em uma linha. Ainda assim, eu não terminei. Como você pode ver, meu comando não fornece grande quantidade de comentário nem saída de status. À parte a falta de erros, é difícil ver se alguma coisa na verdade aconteceu. Agora que já domino a funcionalidade necessária para realizar minha tarefa, posso começar a torná-la com uma aparência melhor, entrando no script de verdade.

Windows PowerShell Prompt Here, por Michael Murgolo

Um dos mais populares Microsoft® PowerToys for Windows® (e um dos meus favoritos) é a ferramenta Open Command Window Here. Disponível como parte do Microsoft PowerToys for Windows XP ou no Windows Server® 2003 Resource Kit Tools, o Open Command Window Here permite que você clique com o botão direito do mouse em uma pasta ou unidade do Windows Explorer para abrir uma janela de comando que aponte para a pasta selecionada.

Como eu estava aprendendo o Windows PowerShell, eu me vi desejando que ele tivesse a mesma funcionalidade. Então peguei o arquivo .inf de configuração do Open Command Window Here e o cmdhere.inf nas ferramentas do Windows Server 2003 Resource Kit e o modifiquei para criar o menu de contexto do Windows PowerShell Prompt Here. Esse arquivo .inf é incluído na postagem original do blog em que se baseia essa barra lateral (disponível em leeholmes.com/blog/PowerShellPromptHerePowerToy.aspx).Para instalar a ferramenta, basta clicar com o botão direito do mouse no arquivo .inf e selecionar Install.

Ao criar a versão dessa ferramenta do Windows PowerShell, descobri que a original tinha um bug - se o Open Command Windows Here fosse desinstalado, ele deixaria para trás uma entrada inativa de menu de contexto. Como resultado, forneci uma versão atualizada do cmdhere.inf, disponível também na postagem original do blog.

Esses PowerToys aproveitam o fato que essas entradas de menu de contexto são configuradas no Registro em chaves associadas aos tipos de objeto Directory e Drive. Isso é feito da mesma maneira com que as ações do menu de contexto são associadas a tipos de arquivo. Por exemplo, quando você clica com o botão direito do mouse em um arquivo .txt no Windows Explorer, obtém diversas ações (como Abrir, Imprimir e Editar) na parte superior da lista. Para entender como esses itens são configurados, vamos dar uma olhada no hive do Registro HKEY_CLASSES_ROOT.

Se você abrir o Editor do Registro e expandir a ramificação HKEY_CLASSES_ROOT, verá chaves nomeadas de tipos de arquivo, como .doc, .txt e assim por diante. Se você clicar na chave .txt, verá que o valor (padrão) é txtfile (veja a Figura A). Esse é o tipo de objeto associado a um arquivo txt. Se você rolar para baixo e expandir a chave txtfile e, em seguida expandir, a chave do shell debaixo dela, verá chaves nomeadas para algumas das entradas do menu de contexto de um arquivo .txt. (Você não verá todos eles porque existem outros métodos para criação de menus de contexto). Em cada um deles está uma chave de comando. O valor (padrão) sob a chave de comando é a linha de comando que o Windows executa quando você seleciona esse item do menu de contexto.

As ferramentas do prompt do cmd e do Windows PowerShell usam essa técnica para configurar as entradas do menu de contexto Open Command Window Here e Windows PowerShell Prompt Here. Não existem tipos de arquivo associados às unidades e aos diretórios, mas existem chaves Drive e Directory em HKEY_CLASSES_ROOT associadas a esses objetos.

Figura A As entradas do menu de contexto são configuradas no Registro

Figura A** As entradas do menu de contexto são configuradas no Registro **(Clique na imagem para aumentar a exibição)

Michael Murgolo é consultor sênior de infra-estrutura da Microsoft Consulting Services. Ele está focado nas áreas de sistemas operacionais, implantação, serviços de rede, Active Directory, gerenciamento de sistemas, automação e gerenciamento de patches.

Criando scripts interativamente

Uma coisa que preciso fazer é ter vários cmdlets executados para cada serviço listado em C:\services.txt. Dessa forma, posso dar saída ao nome do serviço e a qualquer outra informação que eu desejar para poder controlar o progresso do script.

Antes eu usei o pipeline para passar objetos de um cmdlet para outro. Dessa vez, contudo, usarei uma técnica mais parecida com script, chamada construção Foreach (que apresentei na coluna do último mês). A construção Foreach pode aceitar uma coleção de objetos e ela executará vários cmdlets para cada objeto da coleção. Designo uma variável que representa o objeto atual a cada vez pelo loop. Por exemplo, a construção pode começar da seguinte maneira:

foreach ($service in get-content c:\services.txt)

Ainda estou executando aquele mesmo cmdlet Get-Content para recuperar o conteúdo do arquivo de texto. Dessa vez, estou pedindo para que a construção Foreach passe por uma coleção de objetos retornados pelo Get-Content. O loop é executado uma vez em cada objeto e o objeto atual é colocado na variável $service. Agora eu só preciso especificar o código que desejo que seja executado no loop. Começarei tentando duplicar meu comando original de uma linha (isso minimizará a complexidade e assegurará que eu não perca nenhuma funcionalidade):

PS C:\> foreach ($service in get-content c:\services.txt) {
>> set-service $service -startuptype disabled
>> }
>>
PS C:\>

Há algo interessante acontecendo aqui. Observe que eu terminei a primeira linha da construção Foreach com uma chave de abertura. Tudo dentro da chave de abertura e de fechamento é considerado como dentro do loop Foreach e será executado uma vez para cada objeto na coleção de entrada. Após ter digitado { e pressionado Enter, observe que o Windows PowerShell altera o prompt para >> (veja a Figura 2). Isto indica que ele sabe que iniciei uma construção de algum tipo e que está esperando que eu termine. Em seguida, digitei o meu cmdlet Set-Service. Desta vez, usei $service como o primeiro parâmetro, pois $service representa a leitura do nome do serviço atual do meu arquivo de texto. Na próxima linha, concluo a construção com uma chave de fechamento. Após pressionar Enter duas vezes, o Windows PowerShell executa imediatamente o meu código.

Figura 2 O Windows PowerShell sabe que uma construção foi iniciada

Figura 2** O Windows PowerShell sabe que uma construção foi iniciada **(Clique na imagem para aumentar a exibição)

É isso mesmo, imediatamente. O que eu digitei parece um script, mas o Windows PowerShell o está executando, na verdade, ao vivo, e ele não está armazenado em um arquivo de texto de lugar nenhum. Agora digitarei tudo novamente, adicionando uma linha de código para dar saída ao nome do serviço atual:

PS C:\> foreach ($service in get-content c:\services.txt) {
>> set-service $service -startuptype disabled
>> "Disabling $service"
>> }
>>
Disabling messenger
Disabling alerter
PS C:\>

Observe que estou pedindo que ele exiba algo e coloquei esse algo entre aspas duplas. As aspas informam ao Windows PowerShell que se trata de uma seqüência de texto e não de outro comando. Entretanto, quando você usa aspas duplas em vez de aspas simples, o Windows PowerShell examina a seqüência de texto em busca de variáveis. Se ele encontrar alguma, substitui o valor verdadeiro dela pelo nome da variável. Assim, quando ele executa esse código, você pode ver que o nome do serviço atual está sendo exibido.

Isso ainda não é um script!

Até agora, usei o Windows PowerShell interativamente, que é uma ótima forma de ver imediatamente os resultados do que estou escrevendo. Entretanto, digitar novamente essas linhas se tornará, em algum momento, tedioso. É por isso que o Windows PowerShell também pode executar scripts. Na verdade, os scripts do Windows PowerShell seguem a definição mais literal da palavra script: O Windows PowerShell basicamente apenas lê o arquivo de texto de script e “digita” cada linha que encontra, exatamente como se você estivesse digitando as linhas manualmente. O que significa que tudo o que eu fiz até este ponto pode ser colado em um arquivo de script. Portanto, usarei o Bloco de Notas para criar um arquivo chamado disableservices.ps1 e colar dentro dele o seguinte:

foreach ($service in get-content c:\services.txt) {
 set-service $service -startuptype disabled
 "Disabling $service"
 }

Coloquei esse arquivo em uma pasta chamada C;\test. Agora tentarei executá-lo de dentro do Windows PowerShell:

PS C:\test> disableservices
'disableservices' is not recognized as a cmdlet, function, operable program, or
<script file.
At line:1 char:15
+ disableservices <<<<
PS C:\test>

Opa! O que saiu errado? O Windows PowerShell está focado na pasta C:\test folder, mas não encontro meu script. Por quê? Devido a restrições de segurança, o Windows PowerShell foi criado para não executar nenhum script da pasta atual, impedindo, portanto, que qualquer script seqüestre um comando do sistema operacional. Por exemplo, não posso criar um script chamado dir.ps1 e fazer com que ele substitua o comando dir normal. Se eu precisar executar um script da pasta atual, tenho que especificar um caminho relativo:

PS C:\test> ./disableservices
The file C:\test\disableservices.ps1 cannot be loaded. 
The execution of scripts is disabled on this system. 
Please see "get-help about_signing" for more details.
At line:1 char:17
+ ./disableservices <<<<
PS C:\test>

E agora? Ainda não está funcionando. Obtive o caminho certo, mas o Windows PowerShell diz que não é possível executar scripts. Isso ocorre porque, por padrão, o Windows PowerShell não consegue executar scripts. Novamente, trata-se de uma precaução de segurança criada para evitar os problemas que todos nós tivemos com o VBScript. Os scripts mal-intencionados não podem, por padrão, ser executados no Windows PowerShell, porque nenhum script pode ser executado por padrão. Para executar meu script, preciso alterar explicitamente a diretiva de execução:

PS C:\test> set-executionpolicy remotesigned

A diretiva de execução RemoteSigned permite que scripts não assinados sejam executados do computador local – os scripts baixados ainda devem ser assinados para serem executados. A AllSigned é uma diretiva melhor, que executa apenas os scripts que foram assinados digitalmente com um certificado emitido por um editor confiável. Entretanto, eu não tenho um certificado em mãos, assim, não posso assinar meus scripts, o que torna o RemoteSigned uma escolha um tanto melhor para essa situação. Agora tentarei executar meu script novamente:

PS C:\test> ./disableservices
Disabling messenger
Disabling alerter
PS C:\test>

Quero ressaltar que a diretiva de execução RemoteSigned que estamos usando não é uma excelente escolha, mas simplesmente uma boa escolha. Mas existe uma solução bem melhor. Seria bem mais seguro obter um certificado de assinatura de código, usar o cmdlet Set-AuthenticodeSignature do Windows PowerShell para assinar meus scripts e configurar a diretiva de execução para a diretiva AllSigned, muito mais segura.

Há também uma outra diretiva, Unrestricted, que você definitivamente deve evitar. Essa diretiva permite que todos os scripts, mesmo os mal-intencionados de locais remotos, sejam executados sem restrições em seu computador, o que pode colocá-lo em uma posição muito perigosa. Conseqüentemente, não recomendo o uso da diretiva Unrestricted por nenhum motivo.

Resultados imediatos

Os recursos de script interativos do Windows PowerShell permitem que você crie rapidamente protótipos de scripts ou mesmo apenas pequenos blocos de scripts. Você obtém resultados imediatos, assim pode ajustar os scripts para obter exatamente os resultados que deseja. Quando terminar, você poderá mover seu código para um arquivo .ps1, tornando-o permanente e de fácil acesso no futuro. E lembre-se: de preferência, você deverá assinar digitalmente esses arquivos .ps1, de modo a poder deixar o Windows PowerShell configurado para a diretiva de execução AllSigned, a diretiva mais segura que permite a execução de script.

Don Jones é o guru-chefe de scripts da SAPIEN Technologies e co-autor do Windows PowerShell: TFM (consulte www.SAPIENPress.com). Entre em contato com Don em don@sapien.com.

© 2008 Microsoft Corporation e CMP Media, LLC. Todos os direitos reservados. A reprodução parcial ou completa sem autorização é proibida..