Durante o desenvolvimento de projectos, o Visual Studio cria uma série ficheiros temporários para guardar as preferências do utilizador para a solução e para cada um dos projectos (*.suo, *.user, *.cache), ficheiros com informações sobre o sistema de controlo de versões (*.scc, *.vssscc, *.vspscc), além das pastas com os ficheiros gerados durante a compilação e execução de testes (bin, obj, TestResults). Quem utiliza o ReSharper, tem ainda outra pasta com diversos ficheiros temporários (_ReSharper.*) e um ficheiro com preferências do utilizado para a solução (*.ReSharper.user), e o próprio Windows, em pastas que possuem imagens, cria ficheiros com as miniaturas das imagens da pasta (Thumbs.db).
Estes são todos ficheiros que não devemos guardar no sistema de controlo de versões justamente porque são específicos para cada utilizador e são recriados a partir do projecto em cada máquina e, por isso, quando preciso enviar um projecto para alguém (por exemplo, por e-mail), tenho o cuidado de apagar todos estes ficheiros temporários primeiro, para então criar um ficheiro .zip com o conteúdo da pasta do projecto e enviar apenas os ficheiros essenciais para que a outra pessoa consiga abrir o meu projecto/solução no Visual Studio, compilar e executar os testes unitários.
Por ser uma tarefa repetitiva e que faço com alguma frequência, durante muitos anos tive o hábito de manter um ficheiro .cmd na mesma pasta da solução para automatizar isto, mais ou menos assim:
CleanUp.cmd
del /s /f /ah *.suo
del /s /f *.user
del /s /f *.cache
del /s /f /ah *.cache
del /s /f *.scc
del /s /f *.vssscc
del /s /f *.vspscc
rd /s /q bin obj TestResults
rd /s /q _ReSharper.Projecto
Esta solução é bastante simples e funcionou bem durante muito tempo, até que um dia assisti a apresentação do Bruno Lopes sobre PowerShell numa das reuniões da Comunidade NetPonto, onde pude ver o potencial da linguagem e fiquei com vontade de utilizar no meu dia-a-dia.
Desde então, cada vez que preciso escrever um ficheiro .cmd/.bat para automatizar alguma tarefa, tenho feito em PowerShell. Tem sido uma experiência bastante interessante e percebo que começo a ficar cada vez mais à vontade com a sintaxe e, como parte do aprendizado, decidi converter os meus ficheiros .cmd que utilizava para apagar os ficheiros gerados pelo Visual Studio, que mencionei acima, e este foi o resultado:
CleanUp.ps1 (ver gist)
#Lista de pastas que devem ser apagadas
$foldersToRemove =
"bin",
"obj",
"TestResults",
"_ReSharper.*"
#Lista ficheiros que devem ser apagados
$filesToRemove =
"Thumbs.db",
"*.suo",
"*.user",
"*.cache",
"*.scc",
"*.vssscc",
"*.vspscc"
#---- Acção ----
#Apaga todas as pastas definidas na colecção acima
Get-ChildItem .\ -include $foldersToRemove -force -recurse |
where { $_.PsIsContainer } |
foreach ($_) {
Write-Host " A apagar a pasta ./$($_.Name)"
Remove-Item $_.FullName -force -recurse
}
#Apaga todos os ficheiros definidos na colecção acima
Get-ChildItem .\ -include $filesToRemove -force -recurse |
foreach ($_) {
Write-Host " A apagar o ficheiro ./$($_.Name)"
Remove-Item $_.FullName -force -recurse
}
Write-Host "Pronto. Pressione qualquer tecla para fechar..."
[void][System.Console]::ReadKey($true)
Agora, quando quero executar esse script, posso optar por correr directamente via linha de comandos do PowerShell, ou então carregar com o botão direito no ficheiro no Windows Explorer e seleccionar “Run with PowerShell“:

A primeira impressão é que se está a utilizar mais linhas de código para fazer a mesma coisa, e em certa parte é verdade, no entanto este script PowerShell é mais poderoso e mais flexível que o script batch acima, por várias razões:
- Para adicionar novas pastas ou ficheiros para serem apagados, basta adicionar novos itens na variável correspondente, sem ter de incluir novos comandos;
- Não é preciso ter a preocupação de saber se os ficheiros ou pastas a serem apagados estão escondidos (attrib +h) como é o caso dos ficheiros *.cache no script batch;
- É possível apagar pastas utilizando nomes parciais (Ex: _ReSharper.*) enquanto que no script batch era preciso conhecer o nome exacto da pasta, ou então programar um loop semelhante ao script PowerShell para percorrer as pastas e sub-pastas, o que tornaria o script batch mais complexo que o script PowerShell;
- O cmdlet Get-ChildItem permite definir uma lista de exclusão de ficheiros, além da lista normal de inclusão (Ex: -exclude *.txt);
- O cmdlet Get-ChildItem permite utilizar Regular Expressions na definição do filtro (Ex: {$_.Name -match “\d”});
- Cada elemento retornado pelo cmdlet Get-ChildItem é um objecto .NET da classe FileSystemInfo, o que nos permite consultar todas as propriedades destes objectos e facilmente filtrar, por exemplo, pela data de criação (.CreationTime) ou data de alteração (.LastWriteTime) do ficheiro;
- Temos acesso a qualquer classe do .NET a partir desse script, e podemos instanciar objectos. executar métodos, etc…
Outras alternativas
Alguns sistemas de controlo de versão possuem comandos que permitem atingir mais ou menos o mesmo resultado, por exemplo o Git possui o comando git clean, que permite remover os ficheiros que estão a ser ignorados (definido no ficheiro .gitignore), o Mercurial possui um comando semelhante hg purge, e o TortoiseSVN possui uma opção de menu “Delete unversioned files” que também serve o mesmo propósito: Remover os ficheiros que não estão a ser controlados pelo sistema de controlo de versões.
Estes comandos são muito úteis e também os utilizo ocasionalmente (no caso do Git, pelo menos), mas ainda continuo a utilizar os meus scripts PowerShell porque existem situações onde tenho ficheiros que não estou a controlar a versão, mas que desejo partilhar com outras pessoas e não quero que sejam apagados, como é o caso dos ficheiros XML com as opções de publicação dos projectos Web, por exemplo.
Uma outra alternativa é utilizar CleanProject, um pequeno utilitário gratuito desenvolvido pelo Ron Jacobs que integra-se com o Windows Explorer e com o Visual Studio e permite remover essas pastas e ficheiros criados pelo do Visual Studio e ReSharper, e ainda tem a opção de criar um .zip da pasta do projecto automaticamente.