Udostępnij za pośrednictwem


foreach i ForEach-Object

Tym razem bedzie o programowaniu. Za to o programowaniu w PowerShellu, czyli tak bardziej dla administratorów. W ramach jednego z projektów dlubie ostatnio skrypt wczytujacy z pliku dane i na ich podstawie modyfikujacy w srodowisku sporo róznych atrybutów. Ot, klient zazyczyl sobie, zeby konfiguracja byla zgodna z korporacyjnymi regulami i konwencjami i chwala mu za to.

Oprogramowanie tego przez prosty plik EXE wywolujacy wlasciwe funkcje API byloby wzglednie latwe, ale od pewnego czasu tlumacze wszystkim "uczcie sie PowerShella i próbujcie w nim rozwiazywac wasze zadania administracyjne" i sam tez sie do tego próbuje zmuszac. Ma to swój sens, ale to zupelnie inny temat.

W kazdym razie mój skrypt sobie rosnie (glównie ze wzgledu na odczytywanie i ustawianie ACLi) i ma na razie kilkaset linii. Rozmiar wynika równiez w sporym stopniu z tego, ze zalozylem sobie, ze w któryms momencie klient zostanie ze skryptem sam i dobrze byloby, gdyby na ekranie jasno widzial, dlaczego cos nie dziala i co ma zmienic w pliku wejsciowym. Ze wzgledu na obsluge bledów, skrypt wczytuje sobie plik wejsciowy, analizuje go i weryfikuje jego wykonalnosc, po czym albo stwierdza, ze wszystko gra i przystepuje do pracy, albo grzecznie tlumaczy uzytkownikowi, co jest nie tak i konczy dzialanie.

Do analizy danych wejsciowych linia po linii uzylem prostego polecenia

$DaneZPliku | ForEach-Object {$dana=$_.costam; costam; costam}

gdzie $DaneZPliku zostaly chwile wczesniej wczytane. Okazalo sie bardzo szybko, ze pusta linia w takim pliku wejsciowym sprawia, ze $dana ma wartosc $NULL i moja procedura sprawdzajaca mówi (slusznie!), ze to niezbyt dobrze.

Dodalem wiec prosty warunek na poczatku bloku:

$DaneZPliku | ForEach-Object {$dana=$_.costam; if ($dana –eq $NULL) {Continue}; costam; costam}

I tu okazalo sie, ze mam problem. Zamiast (zgodnie z moim naiwnym oczekiwaniem) zaczac znecac sie nad kolejnym obiektem, mój skrypt konczyl dzialanie. Oczywiscie Continue mozna prosto ominac uzywajac If, ale tworzy to dlugie bloki, które potem trudniej wylapuje sie na ekranie. Continue jest milsze. Dlugo myslalem zanim doszedlem do prostego wniosku: tu nie ma petli! Wiec jak Continue ma dzialac poprawnie...? Petle robi sie uzywajac foreach. Zmylil mnie tak naprawde "przyjazny" alias zdefiniowany w PowerShellu...  polecenie "alias foreach" pokaze o co chodzi. Otóz wbrew temu aliasowi, polecenia te nie sa tozsame. Foreach to równoczesnie alias i slowo kluczowe i moze to prowadzic do klopotów.

Jezeli chce uzyc prawdziwej petli, to moje sprawdzanie danych wygladac powinno tak:

foreach ($i in $DaneZPliku) {$dana=$i.costam; if ($dana –eq $NULL) {Continue}; costam; costam}

I to wreszcie dziala jak powinno.

Jako test polecam dwa proste polecenia:

foreach ($i in 'kot','pies','but','krowa') {if ($i -eq 'but') {continue}; write-host $i} – tu mamy petle i wszystko dziala jak powinno, omijajac $i, które nie jest zwierzeciem i kontynuujac dzialanie.

Jezeli jednak napiszemy:

('kot','pies','but','krowa') | ForEach-Object {$i=$_; if ($i -eq 'but') {continue}; write-host $i} – to petli tu nie ma! I Continue bez slowa konczy dzialanie polecenia.

Ten jezyk tak ma i wiem, ze to ja powinienem sie nauczyc a nie mówic, jaki to jest glupi. Choc chetnie zobaczylbym po prostu komunikat o bledzie w sytuacji, gdy uzyje Continue poza petla...

Do zapamietania pozostaje jedno: foreach to zupelnie co innego niz ForEach-Object, a fakt, ze ForEach-Object mozna w dowolnym miejscu zastapic przez foreach, wcale nie upraszcza sytuacji. Najlepiej odzwyczaic sie od uzywania foreach jako aliasu i dzieki temu lepiej czuc gdzie robimy petle, a gdzie wcale nie.

Powershellowo sie ostatnio na blogu zrobilo. Postaram sie napisac cos z innej beczki, ale jak widac takie czasy, ze tak zwany "ITPro" bez PowerShella sie nie obejdzie...

Autor: Grzegorz Tworek [MVP]

Comments

  • Anonymous
    January 01, 2003
    @mgrzeg: jest kilka aspektów...
  1. dlaczego skrypty a nie exe - bo mówimy o zadaniach administracyjnych i skrypty łatwiej utrzymywać niż skompilowane aplikacje
  2. dlaczego uczcie sie - bo PowerShell jest na tyle rozbudowany, że poznanie go, to nie jest jedno popołudnie z książką
  3. dlaczego PowerShell do skryptów - bo wszystko może, a cała jego konkstrukcja jest tak pomyślana, żeby ułatwić życie administratorom IT.
  4. dlaczego się zmuszam - bo niektóre rzeczy prosiłoby się zrobić szybko i na skróty. A próbuję ładnie i dobrze ;)
  • Anonymous
    January 11, 2010
    "Oprogramowanie tego przez prosty plik EXE wywołujący właściwe funkcje API byłoby względnie łatwe, ale od pewnego czasu tłumaczę wszystkim "uczcie się PowerShella i próbujcie w nim rozwiązywać wasze zadania administracyjne" i sam też się do tego próbuję zmuszać. Ma to swój sens, ale to zupełnie inny temat." Grzesiek, mozesz rozwinac te mysl i napisac o motywacji?