Guardar dados em ficheiros é uma tarefa muito comum quando se trabalha com o PowerShell. Pode haver mais opções do que imagina. Vamos começar com o básico e trabalhar nas opções mais avançadas.
- Index
- Trabalhar com caminhos de ficheiros
- Caminho de testes
- Caminho de partição
- Join-Path
- Resolver-Caminho
- Guardar e ler dados
- Reorientação básica com Out-File
- Guardar dados de texto com Add-Content
- Importar dados com Get-Content -Raw
- Guardar dados baseados em colunas com Export-CSV
- -NoTypeInformation
- Guardar dados estruturados com ConvertTo-Json
Guardar dados de objectos ricos com Export-CliXml
- Get-Content -ReadCount
- Leitura mais rápida com System.IO.File
- Escritas com System.IO.StreamWriter
- Saving XML
- Nota rápida sobre codificação
Trabalhar com caminhos de ficheiro
Vamos iniciar isto mostrando-lhe os comandos para trabalhar com caminhos de ficheiro.
Caminho de testes
Test-Path
é um dos comandos mais conhecidos quando se começa a trabalhar com ficheiros. Permite-lhe testar uma pasta ou um ficheiro antes de tentar utilizá-lo.
If( Test-Path -Path $Path ){ Do-Stuff -Path $Path}
Split-Path
Split-Path
irá tomar um caminho completo para um ficheiro e dar-lhe-á o caminho da pasta principal.
PS:> Split-Path -Path 'c:\users\kevin.marquette\documents'c:\users\kevin.marquette
Se precisar do ficheiro ou pasta no fim do caminho, pode usar o argumento -Leaf
para o obter.
PS:> Split-Path -Path 'c:\users\kevin.marquette\documents' -Leafdocuments
Join-Path
Join-Path
pode juntar caminhos de pastas e ficheiros.
PS:> Join-Path -Path $env:temp -ChildPath testingC:\Users\kevin.marquete\AppData\Local\Temp\testing
Utilizo isto sempre que estiver a unir locais que estão armazenados em variáveis. Não tem de se preocupar com a forma de lidar com a barra invertida, porque isto cuida disso por si. Se ambas as suas variáveis tiverem contrabarragens, também se pode classificar isso.
Caminho de Resolução
Resolve-Path
dar-lhe-á o caminho completo para um local. O importante é que expandirá as buscas por wildcard para si. Obterá um conjunto de valores se houver mais do que um matche.
Resolve-Path -Path 'c:\users\*\documents'Path----C:\users\kevin.marquette\DocumentsC:\users\Public\Documents
Que irá enumerar todas as pastas de documentos dos utilizadores locais.
Utilizo-o normalmente em qualquer valor de caminho que obtenho como entrada do utilizador nas minhas funções que aceitam múltiplos ficheiros. Acho que é uma forma fácil de adicionar suporte de wildcard aos parâmetros.
Guardar e ler dados
Agora que temos todos aqueles CmdLets de ajuda fora do caminho, podemos falar sobre as opções que temos para guardar e ler dados.
Utilizo as variáveis $Path
e $Data
para representar o seu caminho de ficheiro e os seus dados nestes exemplos. Faço isto para manter as amostras mais limpas e reflecte melhor como as utilizaria num script.
Reorientação básica com Out-File
PowerShell foi introduzida com Out-File
como a forma de guardar dados em ficheiros. Eis como se parece a ajuda sobre isso.
Get-Help Out-File<#SYNOPSIS Sends output to a file.DESCRIPTION The Out-File cmdlet sends output to a file. You can use this cmdlet instead of the redirection operator (>) when you need to use its parameters.#>
Para qualquer pessoa proveniente de ficheiro de lote, Out-File
é o substituto básico para o operador de redireccionamento >
. Aqui está uma amostra de como utilizá-lo.
'This is some text' | Out-File -FilePath $Path
É um comando básico e já o temos há muito tempo. Aqui está um segundo exemplo que mostra algumas das limitações.
Get-ChildItem | Select-Object Name, Length, LastWriteTime, Fullname | Out-File -FilePath $Path
O ficheiro resultante fica assim quando executado a partir da minha pasta temporária:
Vê-se que a última coluna de valores é encurtada. Out-File
está a processar objectos para a consola mas redirecciona a saída para um ficheiro. Todos os problemas que tiver para formatar na consola irão aparecer no seu ficheiro de saída. A boa notícia é que temos muitas outras opções para isto que irei cobrir abaixo.
Guardar dados de texto com Add-Content
Eu pessoalmente não uso Out-File
e prefiro usar os comandos Add-Content
e Set-Content
. Há também um comando Get-Content
que vai com eles para ler dados de ficheiros.
$data | Add-Content -Path $PathGet-Content -Path $Path
Add-Content
irá criar e anexar aos ficheiros. Set-Content
criará e sobregravará ficheiros.
Estes são bons comandos para todos os fins desde que o desempenho não seja um factor crítico no seu script. São óptimos para pedidos individuais ou pequenos conteúdos. Para grandes conjuntos de dados onde o desempenho é mais importante do que a legibilidade, podemos recorrer à estrutura .Net. Voltarei a este.
Importar dados com Get-Content -Raw
Get-Content
é o comando goto para leitura de dados. Por defeito, este comando irá ler cada linha do ficheiro. Acaba com um conjunto de cordas. Isto também passa cada uma pelo cano abaixo de forma agradável.
O parâmetro -Raw
trará todo o conteúdo como uma string de várias linhas. Isto também é mais rápido porque estão a ser criados menos objectos.
Get-Content -Path $Path -Raw
Guardar dados baseados em colunas com Export-CSV
Se alguma vez precisar de guardar dados para Excel, Export-CSV
é o seu ponto de partida. Isto é bom para armazenar um objecto ou dados estruturados básicos que podem ser importados mais tarde. O formato CSV é valores separados por vírgulas num ficheiro de texto. O Excel é frequentemente o visualizador padrão para ficheiros CSV.
Se quiser importar dados Excel em PowerShell, guarde-os como CSV e depois pode usar Import-CSV
. Existem outras formas de o fazer, mas esta é de longe a mais fácil.
$data | Export-CSV -Path $PathImport-CSV -Path $Path
-NoTypeInformation
Export-CSV
irá inserir informação do tipo na primeira linha do CSV. Se não quiser isso, então pode especificar o parâmetro -NoTypeInformation
.
$data | Export-CSV -Path $Path -NoTypeInformation
Guardar dados de objectos ricos com Export-CliXml
O comando Export-CliXml
é utilizado para guardar objectos completos num ficheiro e depois importá-los novamente com Import-CliXml
. Isto é para objectos com valores aninhados ou datatypes complexos. Os dados em bruto serão um objecto em série verboso em XML. O bom é que se pode guardar um objecto no ficheiro e, quando o importar, receberá esse objecto de volta.
Este formato serializado não é intencional para ser visualizado ou editado directamente. Eis o que o ficheiro date.clixml
se parece:
Não se preocupe em tentar compreendê-lo. Não se pretende escavar nele.
Este é outro comando que não me encontro a usar frequentemente. Se eu tiver um conjunto de dados aninhado ou hierárquico, então JSON é o meu caminho para salvar essa informação.
Guardar dados estruturados com ConvertTo-Json
Quando os meus dados estão aninhados e posso querer editá-los à mão, então uso ConvertTo-Json
para convertê-los para JSON. ConvertFrom-Json
irá convertê-lo de novo num objecto. Estes comandos não guardam nem lêem de ficheiros por si próprios. Terá de recorrer a Get-Content
e Set-Content
para isso.
Há uma coisa importante a notar neste exemplo. Utilizei um para o meu
$Data
mas ConvertFrom-Json
devolve um em vez disso. Isto pode não ser um problema mas não há uma solução fácil para o mesmo.
Também note o uso do Get-Content -Raw
neste exemplo. ConvertFrom-Json
espera uma corda por objecto.
Aqui está o conteúdo do ficheiro JSON de cima:
{ "Address": { "State": "California", "Street": "123 Elm" }}
Verás que este é semelhante ao hashtable original. É por isso que o JSON é um formato popular. É fácil de ler, compreender e editar, se necessário. Utilizo-o sempre para ficheiros de configuração nos meus próprios projectos.
Outras opções e detalhes
Todos estes CmdLets são fáceis de trabalhar. Também temos alguns outros parâmetros e acesso a .Net para mais opções.
Get-Content -ReadCount
The -ReadCount
parâmetro em Get-Content
define quantas linhas que Get-Content
irão ler de uma só vez. Há algumas situações em que isto pode melhorar a sobrecarga de memória de trabalhar com ficheiros maiores.
Isto geralmente inclui a canalização dos resultados para algo que os pode processar à medida que entram e não precisa de manter os dados de entrada.
$dataset = @{}Get-Content -Path $path -ReadCount 15 | Where-Object {$PSItem -match 'error'} | ForEach-Object {$dataset += 1}
Este exemplo contará quantas vezes cada erro aparece no $Path
. Esta conduta pode processar cada linha à medida que é lida a partir do ficheiro.
Pode não ter código que aproveite isto frequentemente, mas esta é uma boa opção a ter em conta.
Leitura mais rápida com System.IO.File
Essa facilidade de utilização que a CmdLets proporciona pode ter um pequeno custo em desempenho bruto. É suficientemente pequeno para que não se note na maior parte do scripting que se faz. Quando esse dia chegar em que precisa de mais velocidade, vai encontrar-se a virar-se para os comandos nativos .Net. Felizmente, são fáceis de trabalhar com.
::ReadAllLines( ( Resolve-Path $Path ) )
Isto é exactamente como Get-Content -Path $Path
na medida em que acabará com uma colecção cheia de cordas. Também pode ler os dados como uma cadeia de caracteres de várias linhas.
::ReadAllText( ( Resolve-Path $Path ) )
O $Path
deve ser o caminho completo ou tentará guardar o ficheiro na sua pasta C:\Windows\System32
. É por isso que utilizo Resolve-Path
neste exemplo.
Aqui está um exemplo de Cmdlet que construí em torno destas chamadas .Net: Import-Content
Escritos com System.IO.StreamWriter
Na mesma nota, também podemos usar System.IO.StreamWriter
para guardar dados. Nem sempre é mais rápido do que os Cmdlets nativos. Esta cai claramente na regra de que se o desempenho é importante, testá-la.
System.IO.StreamWriter
é também um pouco mais complicada do que a CmdLets nativa.
try{ $stream = ::new( $Path ) $data | ForEach-Object{ $stream.WriteLine( $_ ) }}finally{ $stream.close()}
Temos de abrir um StreamWriter
a um $path
. Depois caminhamos os dados e guardamos cada linha para o StreamWriter
. Uma vez terminado, fechamos o ficheiro. Este também requer um caminho completo.
Tive de adicionar manipulação de erros à volta deste para ter a certeza de que o ficheiro estava fechado quando terminámos. Pode querer adicionar um catch
para manipulação de erros personalizados.
Isto deve funcionar muito bem para dados de string. Se tiver problemas, pode querer chamar o método .ToString()
no objecto que está a escrever para o fluxo. Se precisar de mais flexibilidade, basta saber que tem toda a estrutura .Net disponível neste ponto.
Saving XML
Se estiver a trabalhar com ficheiros XML, pode chamar o método Save()
no objecto XML.
$Xml = "<r><data/></r>"$Path = (join-path $pwd 'File.xml')$Xml.Save($Path)
Apenas como os outros métodos .Net em System.IO, é necessário especificar o caminho completo para o ficheiro. Utilizo $pwd
neste exemplo, porque é uma variável automática que contém o resultado de Get-Location
(caminho local).
Nota rápida sobre a codificação
A codificação do ficheiro é a forma como os dados são transformados em binários quando guardados em disco. Na maioria das vezes, apenas funciona, a menos que se faça muito trabalho de plataforma cruzada.
Se se deparar com problemas de codificação, a maioria das CmdLets suportam a especificação da codificação. Se quiser predefinir a codificação para cada comando, pode usar o $PSDefaultParameterValues
hashtable like this:
P>Pode encontrar mais sobre como usar PSDefaultParameterValues no meu post em Hashtables.
Wrapping up
Trabalhar com ficheiros é uma tarefa tão comum que deve dedicar algum tempo a conhecer estas opções. Esperemos que tenha visto algo novo e que o possa utilizar nos seus próprios scripts.
Aqui estão duas funções que implementam as ideias de que falámos