Der abstrakte Syntaxbaum (engl. abstract syntax tree, kurz AST) gliedert den Code in einen hierarchischen Baum, bei dem jedes Element einen Teil des Baumes repräsentiert, und dabei das Skript ‘selbsterkennend’ macht.
Haben Sie den Film Inception gesehen? Er handelt von einem Dieb, der sich in fremde Träume schleusen und aus dem Unterbewusstsein klauen kann. Nach dem Film werden Sie die Realität um sich herum anzweifeln. Er hat mir das gleiche Gefühl wie The Matrix gegeben – dass die Realität nicht das ist, was man denkt sie ist. Das hat nichts mit PowerShell zu tun, gibt Ihnen aber eine Idee davon, wie der PowerShell abstrakte Syntaxbaum (AST) arbeitet.
Stellen Sie sich ein PowerShell-Skript vor, das ‘selbsterkennend’ ist. Stellen Sie sich ein PowerShell-Skript vor, dass sich selbst lesen kann, oder sogar andere Skripte generieren kann, basierend auf seinem eigenen Inhalt. Stellen Sie sich das wie Meta-Skripting vor. Es ist ein tolles Konzept und hat eine Menge praktische Anwendungen! Genau das kann PowerShell AST machen. Der PowerShell AST zerlegt den Code quasi in einen hierarchischen Baum, bei dem jedes Element einen Teil des Baumes repräsentiert.
In diesem Artikel erkläre ich, wie Sie den PowerShell AST benutzen können und gebe einige Beispiele, wie das Parsen von PowerShell-Code funktioniert.
Zu Anfang müssen Sie sich erst einmal mit der System.Management.Automation.Language.Parser class. vertraut machen. Dies ist eine Klasse, die einige statische Methoden enthält, die benutzt werden können, um Skripte und Code zu lesen. Diese Klasse hat zwei Methoden, die Sie regelmäßig benutzen werden - ParseInput() und ParseFile(), die im Grunde das gleiche machen. ParseInput() liest Code als einen langen String während ParseFile() Ihnen dabei hilft, eine Textdatei, die PowerShell-Code enthält, in einen String zum Parsen zu konvertieren. Beide liefern das gleiche Ergebnis.
Nehmen wir zum Beispiel ein einfaches Skript mit den folgenden Linien:
Write-Host 'I am doing something here'
Write-Verbose 'I am doing something here too'
Write-Host 'Again, doing something.'
$var1 = 'abc'
$var2 = '123'
Aus dem Skript heraus möchte ich alle Bezüge auf jedes Cmdlet erkennen, das ich habe und jede Variable. Dazu muss ich einen Weg finden, um den gesamten Skript-Inhalt als einen einzigen String zu bekommen. Das kann man mit dem Merkmal $MyInvocation.MyCommand.ScriptContents machen. Man fügt es als die letzte Zeile im Skript an und führt es aus.
Wenn man den Skript-Inhalt hat, kann man diesen, wie oben erwähnt, an die Methode ParseInput() weitergeben, um einen „Baum“ von dem Skript zu bilden. Ich ersetze dabei $MyInvocation.MyCommand.ScriptContents durch Folgendes:
[System.Management.Automation.Language.Parser]::ParseInput($MyInvocation.MyCommand.ScriptContents, [ref]$null, [ref]$null)
Das gibt mir folgenden Output:
PS> C:\test.ps1
I am doing something here
Again, doing something.
Attributes : {}
UsingStatements : {}
ParamBlock :
BeginBlock :
ProcessBlock :
EndBlock : Write-Host 'I am doing something here'
Write-Verbose 'I am doing something here too'
Write-Host 'Again, doing something.'
$var1 = 'abc'
$var2 = '123'
[System.Management.Automation.Language.Parser]::ParseInput($MyInvocation.MyCommand.ScriptContents,
[ref]$null, [ref]$null)
DynamicParamBlock :
ScriptRequirements :
Extent : Write-Host 'I am doing something here'
Write-Verbose 'I am doing something here too'
Write-Host 'Again, doing something.'
$var1 = 'abc'
$var2 = '123'
[System.Management.Automation.Language.Parser]::ParseInput($MyInvocation.MyCommand.ScriptContents,
[ref]$null, [ref]$null)
Parent :
Das hilft aber noch nicht viel. Man braucht einen Weg, um nur die Funktionen und Variablen zu finden, die im Skript enthalten sind. Dazu ordne ich unseren AST einer Variablen zu, die ich $ast nenne.
PS> $ast = C:\test.ps1
Damit erhalte ich ein Objekt, das verschiedene Methoden und Merkmale enthält, die ich nun benutzen kann.
PS> $ast | gm
TypeName: System.Management.Automation.Language.ScriptBlockAst
Name MemberType Definition
---- ---------- ----------
Copy Method System.Management.Automation.Language.Ast Copy()
Equals Method bool Equals(System.Object obj)
Find Method System.Management.Automation.Language.Ast Find(System.Func[System.Management.Automatio...
FindAll Method System.Collections.Generic.IEnumerable[System.Management.Automation.Language.Ast] Find...
GetHashCode Method int GetHashCode()
GetHelpContent Method System.Management.Automation.Language.CommentHelpInfo GetHelpContent()
GetScriptBlock Method scriptblock GetScriptBlock()
GetType Method type GetType()
SafeGetValue Method System.Object SafeGetValue()
ToString Method string ToString()
Visit Method System.Object Visit(System.Management.Automation.Language.ICustomAstVisitor astVisitor...
Attributes Property System.Collections.ObjectModel.ReadOnlyCollection[System.Management.Automation.Languag...
BeginBlock Property System.Management.Automation.Language.NamedBlockAst BeginBlock {get;}
DynamicParamBlock Property System.Management.Automation.Language.NamedBlockAst DynamicParamBlock {get;}
EndBlock Property System.Management.Automation.Language.NamedBlockAst EndBlock {get;}
Extent Property System.Management.Automation.Language.IScriptExtent Extent {get;}
ParamBlock Property System.Management.Automation.Language.ParamBlockAst ParamBlock {get;}
Parent Property System.Management.Automation.Language.Ast Parent {get;}
ProcessBlock Property System.Management.Automation.Language.NamedBlockAst ProcessBlock {get;}
ScriptRequirements Property System.Management.Automation.Language.ScriptRequirements ScriptRequirements {get;}
UsingStatements Property System.Collections.ObjectModel.ReadOnlyCollection[System.Management.Automation.Languag...
Die nützlichste Methode ist FindAll(). Mit dieser Methode können Sie den AST selbst abfragen, um nach bestimmten Arten von Sprachkonstrukten zu suchen. In unserem Fall suchen wir nach Funktionsaufrufen und Variablenzuweisungen.
Um nur die Sprachkonstrukte zu finden, die wir suchen, müssen wir erst einmal herausfinden, welche Klasse von jedem repräsentiert wird. In unserem Beispiel sind die Klassen CommandAst für Funktionsaufrufe und VariableExpression für Variabelzuweisungen. Sie können alle verschiedenen Klassenarten hier sehen MSDN System.Management.Automation.Language namespace page.
So kann man alle Funktionsreferenzen finden.
PS> $ast.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)
CommandElements : {Write-Host, 'I am doing something here'}
InvocationOperator : Unknown
DefiningKeyword :
Redirections : {}
Extent : Write-Host 'I am doing something here'
Parent : Write-Host 'I am doing something here'
CommandElements : {Write-Verbose, 'I am doing something here too'}
InvocationOperator : Unknown
DefiningKeyword :
Redirections : {}
Extent : Write-Verbose 'I am doing something here too'
Parent : Write-Verbose 'I am doing something here too'
CommandElements : {Write-Host, 'Again, doing something.'}
InvocationOperator : Unknown
DefiningKeyword :
Redirections : {}
Extent : Write-Host 'Again, doing something.'
Parent : Write-Host 'Again, doing something.'
Und auch alle Variabelzuweisungen.
PS> $ast.FindAll({$args[0] -is [System.Management.Automation.Language.VariableExpressionAst]},$true)
ariablePath : var1
Splatted : False
StaticType : System.Object
Extent : $var1
Parent : $var1 = 'abc'
VariablePath : var2
Splatted : False
StaticType : System.Object
Extent : $var2
Parent : $var2 = '123'
VariablePath : MyInvocation
Splatted : False
StaticType : System.Object
Extent : $MyInvocation
Parent : $MyInvocation.MyCommand
VariablePath : null
Splatted : False
StaticType : System.Object
Extent : $null
Parent : [ref]$null
VariablePath : null
Splatted : False
StaticType : System.Object
Extent : $null
Parent : [ref]$null
Sie sehen, dass jedes Konstrukt nun ein Objekt wird, mit dem Sie arbeiten können. Sie haben nun die erforderlichen Kenntnisse, um Ihr Skript in fast jeder erdenklichen Weisen auseinander zu brechen. Durch den Einsatz von AST, können Ihre PowerShell-Skripte so ‘selbsterkennend’ werden.
Tipp: Lesen Sie dazu auch das Whitepaper Automatisieren mit PowerShell oder laden Sie sich kostenfrei eine Testversion von MOVEit Automation, Software für die Automatisierung der Dateiübertragung, herunter.
Adam Bertram is a 20-year veteran of IT. He’s currently an automation engineer, blogger, independent consultant, freelance writer, author, and trainer. Adam focuses on DevOps, system management, and automation technologies as well as various cloud platforms. He is a Microsoft Cloud and Datacenter Management MVP and efficiency nerd that enjoys teaching others a better way to leverage automation.
Lassen Sie sich von unseren Experten beibringen, wie Sie die erstklassigen Funktionen von Sitefinity nutzen können, um überzeugende digitale Erlebnisse zu bieten.
Weitere InformationenAbonnieren Sie, um alle Neuigkeiten, Informationen und Tutorials zu erhalten, die Sie benötigen, um bessere Business-Apps und -Websites zu erstellen