Powershell: Wiele sposobów odczytu i zapisu do plików

Zapisywanie danych do plików jest bardzo częstym zadaniem podczas pracy z PowerShell. Może być więcej opcji niż zdajesz sobie sprawę. Zacznijmy od podstaw i przejdźmy do bardziej zaawansowanych opcji.

  • Index
  • Praca ze ścieżkami do plików
    • Test-Path
    • Split-Path
    • Join-Path
    • Resolve-Path
  • Zapisywanie i odczytywanie danych
    • Podstawowe przekierowanie z Out-File
    • Zapisywanie danych tekstowych z Add-Content
    • Import danych z Get-Content -Raw
    • Zapisz dane oparte na kolumnach za pomocą Export-CSV
      • -NoTypeInformation
    • Zapisz bogate dane obiektowe za pomocą Export-CliXml
    • Zapisz dane strukturalne z ConvertTo-Json
  • Inne opcje i szczegóły
    • Get-Content -ReadCount
    • Szybszy odczyt z System.IO.File
    • Zapisy z System.IO.StreamWriter
    • Zapisywanie XML
    • Krótka uwaga na temat kodowania
  • Zawijanie

Praca ze ścieżkami do plików

Zaczniemy od pokazania poleceń do pracy ze ścieżkami do plików.

Test-Path

Test-Path Jest to jedno z bardziej znanych poleceń, gdy zaczynasz pracę z plikami. Pozwala ono na przetestowanie folderu lub pliku przed próbą jego użycia.

If( Test-Path -Path $Path ){ Do-Stuff -Path $Path}

Split-Path

Split-Path Pobiera pełną ścieżkę do pliku i podaje ścieżkę do folderu nadrzędnego.

PS:> Split-Path -Path 'c:\users\kevin.marquette\documents'c:\users\kevin.marquette

Jeśli potrzebujesz pliku lub folderu na końcu ścieżki, możesz użyć argumentu -Leaf, aby go uzyskać.

PS:> Split-Path -Path 'c:\users\kevin.marquette\documents' -Leafdocuments

Join-Path

Join-Path może łączyć ze sobą ścieżki do folderów i plików.

PS:> Join-Path -Path $env:temp -ChildPath testingC:\Users\kevin.marquete\AppData\Local\Temp\testing

Używam tego za każdym razem, gdy łączę lokalizacje, które są przechowywane w zmiennych. Nie musisz się martwić o to, jak radzić sobie z odwrotnym ukośnikiem, ponieważ to zajmuje się tym za Ciebie. Jeśli twoje zmienne mają w sobie backslashe, to też się tym zajmie.

Resolve-Path

Resolve-Path Poda ci pełną ścieżkę do lokalizacji. Ważną rzeczą jest to, że rozszerzy on wyszukiwania wieloznaczne dla ciebie. Otrzymasz tablicę wartości, jeśli istnieje więcej niż jedno matche.

Resolve-Path -Path 'c:\users\*\documents'Path----C:\users\kevin.marquette\DocumentsC:\users\Public\Documents

To wyliczy wszystkie foldery dokumentów lokalnych użytkowników.

Zwykle używam tego na każdej wartości ścieżki, którą otrzymuję jako dane wejściowe użytkownika do moich funkcji, które akceptują wiele plików. Uważam to za łatwy sposób na dodanie obsługi symboli wieloznacznych do parametrów.

Zapisywanie i odczytywanie danych

Teraz, gdy mamy już wszystkie te pomocnicze CmdLety z drogi, możemy porozmawiać o opcjach, które mamy do zapisywania i odczytywania danych.

Używam zmiennych $Path i $Data do reprezentowania ścieżki do pliku i danych w tych przykładach. Robię to aby zachować czystość przykładów i lepiej odzwierciedlić to jak używałbyś ich w skrypcie.

Podstawowe przekierowania z Out-File

PowerShell został wprowadzony z Out-File jako sposobem na zapisywanie danych do plików. Oto jak wygląda pomoc na ten temat.

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.#>

Dla każdego, kto pochodzi z pliku wsadowego, Out-File jest podstawowym zamiennikiem operatora przekierowania >. Oto przykład, jak go używać.

'This is some text' | Out-File -FilePath $Path

Jest to podstawowe polecenie i mamy je od dawna. Oto drugi przykład, który pokazuje niektóre z ograniczeń.

 Get-ChildItem | Select-Object Name, Length, LastWriteTime, Fullname | Out-File -FilePath $Path

Plik wynikowy wygląda tak, gdy jest wykonywany z mojego folderu temp:

Widzisz, że ostatnia kolumna wartości jest skrócona. Out-File przetwarza obiekty dla konsoli, ale przekierowuje dane wyjściowe do pliku. Wszystkie problemy, które masz z uzyskaniem czegoś do sformatowania w konsoli, pojawią się w twoim pliku wyjściowym. Dobrą wiadomością jest to, że mamy wiele innych opcji, które omówię poniżej.

Zapisywanie danych tekstowych za pomocą Add-Content

Ja osobiście nie używam Out-File i wolę używać komend Add-Content i Set-Content. Istnieje również polecenie Get-Content, które idzie z nimi w parze, aby odczytywać dane z plików.

$data | Add-Content -Path $PathGet-Content -Path $Path

Add-Content będzie tworzyć i dołączać do plików. Set-Content będzie tworzyć i nadpisywać pliki.

To są dobre, uniwersalne polecenia, tak długo jak wydajność nie jest krytycznym czynnikiem w twoim skrypcie. Są one świetne dla pojedynczych lub małych żądań treści. Dla dużych zbiorów danych, gdzie wydajność ma większe znaczenie niż czytelność, możemy zwrócić się do frameworka .Net. Do tego jeszcze wrócę.

Import danych za pomocą Get-Content -Raw

Get-Content to polecenie goto do odczytu danych. Domyślnie, to polecenie będzie czytać każdą linię pliku. Kończysz z tablicą łańcuchów. To również ładnie przekazuje każdy z nich w dół rury.

Parametr -Raw spowoduje, że cała zawartość zostanie przesłana jako wieloliniowy ciąg znaków. To również działa szybciej, ponieważ tworzy się mniej obiektów.

Get-Content -Path $Path -Raw

Zapisywanie danych w kolumnach za pomocą Export-CSV

Jeśli kiedykolwiek będziesz potrzebował zapisać dane do Excela, Export-CSV jest twoim punktem wyjścia. Jest to dobre do przechowywania obiektu lub podstawowych danych strukturalnych, które można później zaimportować. Format CSV to wartości rozdzielone przecinkami w pliku tekstowym. Excel jest często domyślną przeglądarką dla plików CSV.

Jeśli chcesz zaimportować dane Excel w PowerShell, zapisz je jako CSV, a następnie możesz użyć Import-CSV. Są inne sposoby, aby to zrobić, ale ten jest zdecydowanie najłatwiejszy.

$data | Export-CSV -Path $PathImport-CSV -Path $Path

-NoTypeInformation

Export-CSV wstawi informacje o typie do pierwszej linii CSV. Jeśli tego nie chcesz, możesz podać parametr -NoTypeInformation.

$data | Export-CSV -Path $Path -NoTypeInformation

Zapisywanie bogatych danych o obiektach za pomocą Export-CliXml

Komenda Export-CliXml służy do zapisywania pełnych obiektów do pliku, a następnie ponownego ich importowania za pomocą Import-CliXml. To jest dla obiektów z zagnieżdżonymi wartościami lub złożonymi datatypami. Surowe dane będą werbalnie serializowanym obiektem w XML. Fajną rzeczą jest to, że możesz zapisać obiekt do pliku i kiedy go zaimportujesz, otrzymasz ten obiekt z powrotem.

Ten serializowany format nie jest przeznaczony do bezpośredniego przeglądania lub edycji. Oto jak wygląda plik date.clixml:

Nie przejmuj się próbami zrozumienia go. Nie masz zamiaru się w nim grzebać.

Jest to kolejne polecenie, z którego nie korzystam często. Jeśli mam zagnieżdżony lub hierarchiczny zbiór danych, to JSON jest moim goto sposobem na zapisanie tych informacji.

Zapisywanie danych strukturalnych za pomocą ConvertTo-Json

Gdy moje dane są zagnieżdżone i mogę chcieć je edytować ręcznie, wtedy używam ConvertTo-Json aby przekonwertować je na JSON. ConvertFrom-Json przekonwertuje go z powrotem na obiekt. Te polecenia nie zapisują ani nie czytają z plików samodzielnie. Będziesz musiał zwrócić się do Get-Content i Set-Content w tym celu.

Jest jedna ważna rzecz do odnotowania w tym przykładzie. Użyłem dla mojego $Data, ale ConvertFrom-Json zwraca zamiast tego . To może nie być problem, ale nie ma dla niego łatwej poprawki.

Zauważ również użycie Get-Content -Raw w tym przykładzie. ConvertFrom-Json oczekuje jednego łańcucha na obiekt.

Oto zawartość pliku JSON z powyższego przykładu:

{ "Address": { "State": "California", "Street": "123 Elm" }}

Zauważysz, że jest to podobne do oryginalnego hashtable. To właśnie dlatego JSON jest popularnym formatem. Jest łatwy do odczytania, zrozumienia i edycji w razie potrzeby. Używam tego cały czas dla plików konfiguracyjnych w moich własnych projektach.

Inne opcje i szczegóły

Wszystkie te CmdLety są łatwe do pracy. Mamy również kilka innych parametrów i dostęp do .Net dla większej ilości opcji.

Get-Content -ReadCount

Parametr -ReadCount na Get-Content określa ile linii, które Get-Content będą czytane na raz. Istnieją sytuacje, w których może to poprawić koszty pamięci przy pracy z większymi plikami.

To generalnie obejmuje przesyłanie wyników do czegoś, co może je przetwarzać w miarę ich napływania i nie musi przechowywać danych wejściowych.

$dataset = @{}Get-Content -Path $path -ReadCount 15 | Where-Object {$PSItem -match 'error'} | ForEach-Object {$dataset += 1}

Ten przykład będzie liczył, ile razy każdy błąd pojawia się w $Path. Ten potok może przetwarzać każdą linię tak, jak jest ona odczytywana z pliku.

Możesz nie mieć kodu, który wykorzystuje to często, ale jest to dobra opcja, aby być świadomym.

Szybsze odczyty z System.IO.File

Łatwość użycia, którą zapewniają CmdLety może przyjść małym kosztem w surowej wydajności. Jest on na tyle mały, że nie zauważysz go w większości skryptów, które wykonujesz. Gdy nadejdzie dzień, w którym będziesz potrzebował większej szybkości, zwrócisz się w stronę natywnych poleceń .Net. Na szczęście są one łatwe do pracy.

::ReadAllLines( ( Resolve-Path $Path ) )

To jest tak samo jak Get-Content -Path $Path w tym, że skończysz z kolekcją pełną łańcuchów. Możesz również odczytać dane jako ciąg wielowierszowy.

::ReadAllText( ( Resolve-Path $Path ) )

The $Path musi być pełną ścieżką lub spróbuje zapisać plik w twoim folderze C:\Windows\System32. Dlatego właśnie używam Resolve-Path w tym przykładzie.

Tutaj jest przykładowy Cmdlet, który zbudowałem wokół tych wywołań .Net: Import-Content

Writes with System.IO.StreamWriter

W tej samej notatce, możemy również użyć System.IO.StreamWriter do zapisywania danych. Nie zawsze jest to szybsze niż natywne Cmdlets. Ten wyraźnie podpada pod zasadę, że jeśli wydajność ma znaczenie, przetestuj go.

System.IO.StreamWriter jest również nieco bardziej skomplikowany niż natywne CmdLety.

try{ $stream = ::new( $Path ) $data | ForEach-Object{ $stream.WriteLine( $_ ) }}finally{ $stream.close()}

Musimy otworzyć StreamWriter do $path. Następnie przechodzimy przez dane i zapisujemy każdą linię do StreamWriter. Gdy skończymy, zamykamy plik. Ten również wymaga pełnej ścieżki.

Musiałem dodać obsługę błędów wokół tego, aby upewnić się, że plik został zamknięty, gdy skończyliśmy. Możesz chcieć dodać catch do niestandardowej obsługi błędów.

To powinno działać bardzo dobrze dla danych łańcuchowych. Jeśli masz problemy, możesz chcieć wywołać metodę .ToString() na obiekcie, który zapisujesz do strumienia. Jeśli potrzebujesz większej elastyczności, wiedz, że w tym momencie masz do dyspozycji cały framework .Net.

Zapisywanie XML

Jeśli pracujesz z plikami XML, możesz wywołać metodę Save() na obiekcie XML.

$Xml = "<r><data/></r>"$Path = (join-path $pwd 'File.xml')$Xml.Save($Path)

Tak jak w przypadku innych metod .Net w System.IO, musisz podać pełną ścieżkę do pliku. Używam $pwd w tym przykładzie, ponieważ jest to zmienna automatyczna, która zawiera wynik Get-Location (ścieżka lokalna).

Krótka uwaga na temat kodowania

Kodowanie pliku to sposób, w jaki dane są przekształcane w binarne podczas zapisywania na dysku. W większości przypadków to po prostu działa, chyba że wykonujesz dużo pracy na różnych platformach.

Jeśli napotkasz problemy z kodowaniem, większość CmdLetów obsługuje określanie kodowania. Jeśli chcesz ustawić domyślne kodowanie dla każdej komendy, możesz użyć $PSDefaultParameterValues hashtable jak poniżej:

Więcej o tym jak używać PSDefaultParameterValues znajdziesz w moim poście o Hashtables.

Podsumowanie

Praca z plikami jest tak powszechnym zadaniem, że powinieneś poświęcić czas na poznanie tych opcji. Mam nadzieję, że zobaczyłeś coś nowego i możesz to wykorzystać w swoich własnych skryptach.

Oto dwie funkcje, które implementują pomysły, o których mówiliśmy

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *