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