Komenda poszukiwana
PowerShell od wersji trzeciej oferuje stosunkowo łatwy dostęp do Abstract Syntax Tree (AST – drzewo składniowe, bądź drzewo składni abstrakcyjnej) samego języka. Dzięki temu narzędzia, ale również użytkownicy mogą przeprowadzać analizę składni, odnajdując jej elementy nie za pomocą wyrażeń regularnych (które czasem mogą wprowadzić nas w błąd) a za pomocą analizy zbliżonej do tej, jaką przeprowadzi sam silnik. Umożliwia to odnajdowanie elementów składni wszędzie tam, gdzie one wystąpią, bez ryzyka, że coś pominiemy (lub w naszych wynikach pojawią się wystąpienia nieprawidłowe). Przykład problemu, który AST pozwoli rozwiązać, miałem ostatnio w pracy.
Problem był stosunkowo trywialny. Oto nasz nowy kolega, poznawszy magię splattingu, zaczął go używać w większości pisanego przez siebie kodu. Niestety, jedna z jego modyfikacji doprowadziła do błędu, który z początku trudno było wyjaśnić:
Skąd nam się tu urodziło polecenie “Stop”? Mogliśmy oczywiście zatrudnić wyrażenia regularne, ale skorzystałem z okazji, by nauczyć kolegę korzyści z AST. Wiedzieliśmy, że polecenie zdefiniowane musi być w jednej z funkcji, których używaliśmy. Tych definicję przechowujemy w plikach *.ps1 w naszym repo. Pora więc znaleźć winnego. Wszystko za pomocą jednolinijkowca (którego na potrzeby artykułu rozpisałem na linii kilka):
using namespace System.Management.Automation.Language | |
$znajda = @((Get-ChildItem -Path *\*.ps1 | Convert-Path).ForEach{ | |
( | |
[Parser]::ParseFile($_, [ref]$null, [ref]$null) | |
).Find( | |
{ | |
$args[0] -is [CommandAst] -and | |
$args[0].CommandElements[0].Value -eq 'Stop' | |
}, | |
$true | |
) | |
}).Extent[0] | |
$start = $znajda.StartLineNumber - 2 | |
$end = $znajda.EndLineNumber + 2 | |
(Get-Content -Path $znajda.File)[$start..$end] | |
# Na wyjściu... | |
$splat = @{ | |
ErrorAction = Stop | |
Verbose = $true | |
} | |
Get-Process -Id $PID @splat |
Cóż się wydarzyło? Przenosząc parametr ErrorAction to tablicy skrótów kolega zapomniał otoczyć wartość ‘Stop’ cudzysłowami, przez co PowerShell uznał, że chcemy uruchomić polecenie Stop. Jeden PR później wszystko wróciło do normy. Nie ma to jak strzelać do muszki za pomocą dubeltówki!