Centrum skryptów - Systemy operacyjne

Jak wczytać plik CSV?

Udostępnij na: Facebook

Skrypciarze odpowiadają na Wasze pytania

Cześć Skrypciarze!

Witamy w rubryce TechNet, w której Skrypciarze z firmy Microsoft odpowiadają na częste pytania dotyczące używania skryptów w administracji systemu. Jeśli macie jakieś pytania z tej dziedziny, zachęcamy do wysłania e-maila na adres: scripter@microsoft.com. Nie możemy zagwarantować odpowiedzi na każde otrzymane pytanie, ale staramy się jak możemy.

Jak wczytać plik CSV?

Cześć Skrypciarze! Pytanie

Cześć, Skrypciarze! Dopiero zaczynam swoją przygodę ze skryptami, mimo to przydzielono mi zadanie stworzenia skryptu, który wczytuje plik CSV, a następnie realizuje operację na każdej linii pliku. Nie mam pojęcia, jakiego cmdletu lub jakiej akcji użyć. Czy możecie wskazać mi odpowiedni kierunek?

-DF

Cześć Skrypciarze! Pytanie

Cześć, DF,

Wskazać odpowiedni kierunek? Jasne. Jak zwykł mawiać jeden z moich wykładowców "Na zachód, młody człowieku" (nie, nie wywodzę się z XIX wieku, profesor przytaczał słowa innej osobistości). Ponieważ pytałeś o cmdlet, wygląda na to, że próbujesz użyć skryptu Windows PowerShell. Niewątpliwą zaletą powłoki Windows PowerShell jest dostępność cmdletu o nazwie Import-CSV, który automatycznie odczytuje plik CSV (wartości rozdzielone przecinkami). Rozwiązanie to jest dużo prostsze niż skrypt VBScript, który wymagałby stworzenia instancji obiektu FileSystemObject, dokonania iteracji po tym obiekcie, użycia funkcji split do podziału linii i zapisania jej w tablicy, a następnie przetworzenia każdego elementu. Wiele przykładów zastosowania tej techniki znajduje się w Centrum skryptów.

Aby użyć cmdletu Import-CSV, musimy najpierw przekazać do niego ścieżkę do pliku tekstowego. Następnie używamy cmdletu ForEach-Object do przejścia przez tablicę tekstu i uzyskujemy dostęp do poszczególnych wartości przy pomocy nagłówków kolumn. W pętli ForEach możemy w dowolny sposób przetwarzać wartości pobierane z pliku CSV. Powyższa metoda została zademonstrowana w skrypcie ReadCsvDoWmi.ps1, w którym odczytujemy tekstowy plik CSV zawierający spis klas WMI oraz listę nazw komputerów, a następnie uruchamiamy szereg kwerend WMI. Skrypt ReadCsvDoWmi.ps1 został zaprezentowany poniżej:

$path = "c:\fso\Classes.csv"

Import-csv -path $path | 

foreach-object `

{ 

  Get-WmiObject -class $_.class -computername $_.computername 

}

Plik CSV, który mamy zamiar wykorzystać w skrypcie ReadCSVDoWmi.ps1, został pokazany na Rysunku 1. Jak widać, pierwszy wiersz pliku zawiera nagłówki kolumn - w tym przypadku dwie wartości: Class oraz ComputerName. Wartości znajdujące się w kolumnie klas (WIN32_Bios, WIN32_Bus, WIN32_UsbHub oraz WIN32_Share) to jedynie kilka z ponad 1000 klas WMI dostępnych w standardowej instalacji dowolnej nowoczesnej wersji systemu Microsoft Windows.

A tak na marginesie, być może zastanawiacie się, dlaczego wybrałem właśnie te klasy. Odpowiedź jest prosta: mają one jedne z najkrótszych nazw spośród klas WMI należących do domyślnej przestrzeni nazw WMI root\cimv2 i z tego względu łatwo je wpisać. Oto polecenie PowerShell, które posłużyło mi do odnalezienia klas WMI o krótkich nazwach:

Get-WmiObject -List | Where-Object { $_.name.length -le 11 -AND $_.name -notmatch '^cim' }

Aby ułatwić testowanie skryptu na pojedynczym komputerze, zastosowaliśmy różne nazwy lokalnego komputera: localhost, loopback oraz adres IP dla loopback, 127.0.0.1. A oto wspomniany plik CSV stanowiący majstersztyk pomysłowości wynikającej z lenistwa:

Rysunek 1: Plik CSV wykorzystany w skrypcie ReadCsvDoWmi.ps1.

A teraz przyjrzyjmy się dokładniej skryptowi ReadCsvDoWmi.ps1. Rozpoczyna się on od prostego przypisania wartości do zmiennej. Wszystkie zmienne w Windows PowerShell rozpoczynają się od znaku dolara (co znacznie obniża ich wartość poza granicami USA). Dzięki zastosowaniu tej konwencji gdy tylko widzimy element rozpoczynający się od znaku dolara, od razu wiemy, że jest to zmienna. Gdy prowadziłem wykłady dotyczące języka VBScript, zauważyłem, że początkujący studenci często miewali problemy z rozróżnieniem instrukcji języka, identyfikatorów programu oraz zmiennych. Dlaczego? Przede wszystkim dlatego, że w przypadku zastosowania węgierskiego standardu nazewnictwa wszystkie zmienne wyglądają jak dziwnie elementy programistyczne. Nikt celowo nie stworzyłby zmiennej o nazwie objfso, nieprawdaż? Zmienne, z jakimi mieliśmy do czynienia na algebrze, nosiły nazwy takie jak x, y czy z. W Windows PowerShell unikamy tego typu nieporozumień, gdyż wszystkie zmienne rozpoczynają się od znaku dolara. A zatem tworzymy zmienną o nazwie $ path i przypisujemy do niej ścieżkę do pliku CSV:

$path = "c:\fso\Classes.csv"

Teraz nadeszła pora na odczytanie pliku CSV. Do tego celu wykorzystujemy cmdlet Import-CSV wraz ze ścieżką do pliku CSV. Zakańczamy linię kodu przy pomocy znaku potoku ("|"). Znak potoku w skrypcie Windows PowerShell służy do przekazania danych z pliku CSV do następnego polecenia. Omawiana linia kodu została zaprezentowana poniżej:

Import-csv -path $path |

Zaletami wykorzystania potoku jest szybkość i efektywność oraz mniejsze zużycie pamięci. Jest to bardzo istotne w przypadku odczytywania plików, które są większe niż nasz przykładowy plik classes.csv. Tradycyjne podejście do problemu odczytu pliku CSV ilustruje skrypt ReadCsvDoWmiDoNotUsePipeline.ps1 zaprezentowany pod tym paragrafem. W tym skrypcie odczytujemy zawartość pliku CSV, a dane wynikowe przechowujemy w zmiennej $csv. W konsekwencji nie możemy przejść do kolejnej linii kodu, zanim nie zakończymy procesu wczytywania całego pliku CSV. Takie rozwiązanie wiąże się również z umieszczeniem w pamięci całej zawartości pliku CSV. Po zakończeniu tej operacji dokonywana jest iteracja podobna do zastosowanej w naszym oryginalnym skrypcie:

ReadCsvDoWmiDoNotUsePipeline.ps1

$path = "c:\fso\Classes.csv"

$csv = Import-csv -path $path

foreach($line in $csv)

{

Get-WmiObject -class $line.class -computername $line.computername

}

Natomiast dzięki wykorzystaniu potoku możemy rozpocząć przetwarzanie zawartości pliku CSV już w czasie pobierania jej z twardego dysku. To oznacza, że skrypt może szybciej zakończyć działanie oraz że wykorzysta mniej pamięci do przetwarzania dużych plików CSV. W przypadku naszego małego pliku CSV wykonanie "gorszego" skryptu ReadCsvDoWmiDoNotUsePipeline.ps1 trwa dłużej niż wykonanie skryptu wykorzystującego potok. Jednak nie są to znaczące różnice, co widać na poniższym rysunku:

Rysunek 2: Względne czasy wykonania dwóch skryptów w milisekundach.

Być może niektórzy z Was pomyślą: "Rewelacja! Mogę wykorzystać to jako argument w mojej oceny pracowniczej. Zmodyfikowałem skrypt ReadCsvDoWmiDoNotUsePipeline.ps1 i uzyskałem 6 procentowy wzrost wydajności". Jednak musimy mieć na uwadze, że nie można całkowicie polegać na podanych w milisekundach wynikach, jakie zwraca cmdlet Measure-Command. Po prostu nie są one wystarczająco dokładne. Za każdym razem, gdy uruchamiamy dwa omawiane skrypty z wykorzystaniem tego samego pliku CSV, możemy otrzymać inne wyniki.

Następnie przesyłamy potokiem dane wyjściowe cmdletu Import-CSV do cmdletu Foreach-Object. Wykorzystujemy znak kontynuacji linii (akcent słaby, lewy apostrof, odwrotny apostrof czy jakkolwiek go zwał), aby móc dokończyć polecenie w następnej linii. Dzięki temu możemy rozmieścić nawiasy klamrowe w osobnych liniach (co zwiększa czytelność skryptu). Oto wspomniana linia kodu:

foreach-object `

Kolejna linia zawiera nawias klamrowy (płacą mi od linii):

{

A teraz możemy wykonać dowolne operacje na wartościach pobranych z pliku CSV. W tym przykładzie wykonujemy kwerendy WMI poprzez badanie poszczególnych klas WMI na różnych komputerach. Do tego celu wykorzystujemy cmdlet Get-WmiObject. Jak można zauważyć, w stworzonym przeze mnie przykładzie nagłówki kolumn w pliku CSV mają takie same nazwy jak parametry, które planowałem wykorzystać w poleceniu cmdlet Get-WmiObject. To rozwiązanie poprawia czytelność kodu i reprezentuje podejście, które zalecam w mojej książce PowerShellBest Practices.

Get-WmiObject -class $_.class -computername $_.computername

Następnie (jak można się spodziewać) pojawia się kolejny nawias klamrowy:

}

Mam nadzieję, że omówiony przykład pomoże Wam rozpocząć przygodę z wczytywaniem plików CSV w powłoce Windows PowerShell. Technika, która umożliwia przechowywanie w pliku CSV nazw komputerów i klas WMI oraz realizowanie na nich wybranych kwerend, stanowi interesującą koncepcję, którą można dowolnie dostosowywać do własnych potrzeb.

Ed Wilson i Craig Liebendorfer, Skrypciarze

 Do początku strony Do początku strony

Centrum skryptów - Systemy operacyjne