Powershell: As muitas formas de ler e escrever em ficheiros

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 de objectos ricos com Export-CliXml

  • Guardar dados estruturados com ConvertTo-Json
  • Outras opções e detalhes
    • Get-Content -ReadCount
    • Leitura mais rápida com System.IO.File
    • Escritas com System.IO.StreamWriter
    • Saving XML
    • Nota rápida sobre codificação
  • Embrulhar
  • 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

    Deixe uma resposta

    O seu endereço de email não será publicado. Campos obrigatórios marcados com *