MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Management.Automation.Runspaces;5 6using Dbg = System.Management.Automation.Diagnostics;7 8namespace System.Management.Automation9{10 /// <summary>11 /// Defines a PowerShell command / script object which can be used with12 /// <see cref="PowerShell"/> object.13 /// </summary>14 public sealed class PSCommand15 {16 #region Private Fields17 18 private PowerShell _owner;19 private CommandCollection _commands;20 private Command _currentCommand;21 22 #endregion23 24 #region Constructor25 26 /// <summary>27 /// Creates an empty PSCommand; a command or script must be added to this PSCommand before it can be executed.28 /// </summary>29 public PSCommand()30 {31 Initialize(null, false, null);32 }33 34 /// <summary>35 /// Internal copy constructor.36 /// </summary>37 /// <param name="commandToClone"></param>38 internal PSCommand(PSCommand commandToClone)39 {40 _commands = new CommandCollection();41 foreach (Command command in commandToClone.Commands)42 {43 Command clone = command.Clone();44 // Attach the cloned Command to this instance.45 _commands.Add(clone);46 _currentCommand = clone;47 }48 }49 50 /// <summary>51 /// Creates a PSCommand from the specified command.52 /// </summary>53 /// <param name="command">Command object to use.</param>54 internal PSCommand(Command command)55 {56 _currentCommand = command;57 _commands = new CommandCollection();58 _commands.Add(_currentCommand);59 }60 61 #endregion62 63 #region Command / Parameter Construction64 65 /// <summary>66 /// Add a command to construct a command pipeline.67 /// For example, to construct a command string "get-process | sort-object",68 /// <code>69 /// PSCommand command = new PSCommand("get-process").AddCommand("sort-object");70 /// </code>71 /// </summary>72 /// <param name="command">73 /// A string representing the command.74 /// </param>75 /// <exception cref="InvalidPowerShellStateException">76 /// Powershell instance cannot be changed in its77 /// current state.78 /// </exception>79 /// <returns>80 /// A PSCommand instance with <paramref name="command"/> added.81 /// </returns>82 /// <remarks>83 /// This method is not thread safe.84 /// </remarks>85 /// <exception cref="ArgumentNullException">86 /// cmdlet is null.87 /// </exception>88 public PSCommand AddCommand(string command)89 {90 if (command == null)91 {92 throw PSTraceSource.NewArgumentNullException(nameof(command));93 }94 95 _owner?.AssertChangesAreAccepted();96 97 _currentCommand = new Command(command, false);98 _commands.Add(_currentCommand);99 100 return this;101 }102 103 /// <summary>104 /// Add a cmdlet to construct a command pipeline.105 /// For example, to construct a command string "get-process | sort-object",106 /// <code>107 /// PSCommand command = new PSCommand("get-process").AddCommand("sort-object");108 /// </code>109 /// </summary>110 /// <param name="cmdlet">111 /// A string representing cmdlet.112 /// </param>113 /// <param name="useLocalScope">114 /// if true local scope is used to run the script command.115 /// </param>116 /// <exception cref="InvalidPowerShellStateException">117 /// Powershell instance cannot be changed in its118 /// current state.119 /// </exception>120 /// <returns>121 /// A PSCommand instance with <paramref name="cmdlet"/> added.122 /// </returns>123 /// <remarks>124 /// This method is not thread safe.125 /// </remarks>126 /// <exception cref="ArgumentNullException">127 /// cmdlet is null.128 /// </exception>129 public PSCommand AddCommand(string cmdlet, bool useLocalScope)130 {131 if (cmdlet == null)132 {133 throw PSTraceSource.NewArgumentNullException(nameof(cmdlet));134 }135 136 _owner?.AssertChangesAreAccepted();137 138 _currentCommand = new Command(cmdlet, false, useLocalScope);139 _commands.Add(_currentCommand);140 141 return this;142 }143 144 /// <summary>145 /// Add a piece of script to construct a command pipeline.146 /// For example, to construct a command string "get-process | foreach { $_.Name }"147 /// <code>148 /// PSCommand command = new PSCommand("get-process")149 /// .AddScript("foreach { $_.Name }", true);150 /// </code>151 /// </summary>152 /// <param name="script">153 /// A string representing the script.154 /// </param>155 /// <returns>156 /// A PSCommand instance with <paramref name="script"/> added.157 /// </returns>158 /// <remarks>159 /// This method is not thread-safe.160 /// </remarks>161 /// <exception cref="ArgumentNullException">162 /// command is null.163 /// </exception>164 /// <exception cref="InvalidPowerShellStateException">165 /// Powershell instance cannot be changed in its166 /// current state.167 /// </exception>168 public PSCommand AddScript(string script)169 {170 if (script == null)171 {172 throw PSTraceSource.NewArgumentNullException(nameof(script));173 }174 175 _owner?.AssertChangesAreAccepted();176 177 _currentCommand = new Command(script, true);178 _commands.Add(_currentCommand);179 180 return this;181 }182 183 /// <summary>184 /// Add a piece of script to construct a command pipeline.185 /// For example, to construct a command string "get-process | foreach { $_.Name }"186 /// <code>187 /// PSCommand command = new PSCommand("get-process")188 /// .AddScript("foreach { $_.Name }", true);189 /// </code>190 /// </summary>191 /// <param name="script">192 /// A string representing the script.193 /// </param>194 /// <param name="useLocalScope">195 /// if true local scope is used to run the script command.196 /// </param>197 /// <returns>198 /// A PSCommand instance with <paramref name="script"/> added.199 /// </returns>200 /// <remarks>201 /// This method is not thread-safe.202 /// </remarks>203 /// <exception cref="ArgumentNullException">204 /// command is null.205 /// </exception>206 /// <exception cref="InvalidPowerShellStateException">207 /// Powershell instance cannot be changed in its208 /// current state.209 /// </exception>210 public PSCommand AddScript(string script, bool useLocalScope)211 {212 if (script == null)213 {214 throw PSTraceSource.NewArgumentNullException(nameof(script));215 }216 217 _owner?.AssertChangesAreAccepted();218 219 _currentCommand = new Command(script, true, useLocalScope);220 _commands.Add(_currentCommand);221 222 return this;223 }224 225 /// <summary>226 /// Add a <see cref="Command"/> element to the current command227 /// pipeline.228 /// </summary>229 /// <param name="command">230 /// Command to add.231 /// </param>232 /// <returns>233 /// A PSCommand instance with <paramref name="command"/> added.234 /// </returns>235 /// <remarks>236 /// This method is not thread-safe.237 /// </remarks>238 /// <exception cref="ArgumentNullException">239 /// command is null.240 /// </exception>241 /// <exception cref="InvalidPowerShellStateException">242 /// Powershell instance cannot be changed in its243 /// current state.244 /// </exception>245 public PSCommand AddCommand(Command command)246 {247 if (command == null)248 {249 throw PSTraceSource.NewArgumentNullException(nameof(command));250 }251 252 _owner?.AssertChangesAreAccepted();253 254 _currentCommand = command;255 _commands.Add(_currentCommand);256 257 return this;258 }259 260 /// <summary>261 /// Add a parameter to the last added command.262 /// For example, to construct a command string "get-process | select-object -property name"263 /// <code>264 /// PSCommand command = new PSCommand("get-process")265 /// .AddCommand("select-object")266 /// .AddParameter("property", "name");267 /// </code>268 /// </summary>269 /// <param name="parameterName">270 /// Name of the parameter.271 /// </param>272 /// <param name="value">273 /// Value for the parameter.274 /// </param>275 /// <returns>276 /// A PSCommand instance with <paramref name="parameterName"/> added277 /// to the parameter list of the last command.278 /// </returns>279 /// <remarks>280 /// This method is not thread safe.281 /// </remarks>282 /// <exception cref="ArgumentException">283 /// Name is non null and name length is zero after trimming whitespace.284 /// </exception>285 /// <exception cref="InvalidPowerShellStateException">286 /// Powershell instance cannot be changed in its287 /// current state.288 /// </exception>289 public PSCommand AddParameter(string parameterName, object value)290 {291 if (_currentCommand == null)292 {293 throw PSTraceSource.NewInvalidOperationException(PSCommandStrings.ParameterRequiresCommand,294 new object[] { "PSCommand" });295 }296 297 _owner?.AssertChangesAreAccepted();298 299 _currentCommand.Parameters.Add(parameterName, value);300 return this;301 }302 303 /// <summary>304 /// Adds a switch parameter to the last added command.305 /// For example, to construct a command string "get-process | sort-object -descending"306 /// <code>307 /// PSCommand command = new PSCommand("get-process")308 /// .AddCommand("sort-object")309 /// .AddParameter("descending");310 /// </code>311 /// </summary>312 /// <param name="parameterName">313 /// Name of the parameter.314 /// </param>315 /// <returns>316 /// A PSCommand instance with <paramref name="parameterName"/> added317 /// to the parameter list of the last command.318 /// </returns>319 /// <remarks>320 /// This method is not thread safe.321 /// </remarks>322 /// <exception cref="ArgumentException">323 /// Name is non null and name length is zero after trimming whitespace.324 /// </exception>325 /// <exception cref="InvalidPowerShellStateException">326 /// Powershell instance cannot be changed in its327 /// current state.328 /// </exception>329 public PSCommand AddParameter(string parameterName)330 {331 if (_currentCommand == null)332 {333 throw PSTraceSource.NewInvalidOperationException(PSCommandStrings.ParameterRequiresCommand,334 new object[] { "PSCommand" });335 }336 337 _owner?.AssertChangesAreAccepted();338 339 _currentCommand.Parameters.Add(parameterName, true);340 return this;341 }342 343 /// <summary>344 /// Adds a <see cref="CommandParameter"/> instance to the last added command.345 /// </summary>346 internal PSCommand AddParameter(CommandParameter parameter)347 {348 if (_currentCommand == null)349 {350 throw PSTraceSource.NewInvalidOperationException(PSCommandStrings.ParameterRequiresCommand,351 new object[] { "PSCommand" });352 }353 354 _owner?.AssertChangesAreAccepted();355 356 _currentCommand.Parameters.Add(parameter);357 return this;358 }359 360 /// <summary>361 /// Adds an argument to the last added command.362 /// For example, to construct a command string "get-process | select-object name"363 /// <code>364 /// PSCommand command = new PSCommand("get-process")365 /// .AddCommand("select-object")366 /// .AddArgument("name");367 /// </code>368 /// This will add the value "name" to the positional parameter list of "select-object"369 /// cmdlet. When the command is invoked, this value will get bound to positional parameter 0370 /// of the "select-object" cmdlet which is "Property".371 /// </summary>372 /// <param name="value">373 /// Value for the parameter.374 /// </param>375 /// <returns>376 /// A PSCommand instance parameter value <paramref name="value"/> added377 /// to the parameter list of the last command.378 /// </returns>379 /// <exception cref="InvalidPowerShellStateException">380 /// Powershell instance cannot be changed in its381 /// current state.382 /// </exception>383 /// <remarks>384 /// This method is not thread safe.385 /// </remarks>386 public PSCommand AddArgument(object value)387 {388 if (_currentCommand == null)389 {390 throw PSTraceSource.NewInvalidOperationException(PSCommandStrings.ParameterRequiresCommand,391 new object[] { "PSCommand" });392 }393 394 _owner?.AssertChangesAreAccepted();395 396 _currentCommand.Parameters.Add(null, value);397 return this;398 }399 400 /// <summary>401 /// Adds an additional statement for execution402 ///403 /// For example,404 /// <code>405 /// Runspace rs = RunspaceFactory.CreateRunspace();406 /// PowerShell ps = PowerShell.Create();407 ///408 /// ps.Runspace = rs;409 /// ps.AddCommand("Get-Process").AddArgument("idle");410 /// ps.AddStatement().AddCommand("Get-Service").AddArgument("audiosrv");411 /// ps.Invoke();412 /// </code>413 /// </summary>414 /// <returns>415 /// A PowerShell instance with the items in <paramref name="parameters"/> added416 /// to the parameter list of the last command.417 /// </returns>418 public PSCommand AddStatement()419 {420 if (_commands.Count == 0)421 {422 return this;423 }424 425 _commands[_commands.Count - 1].IsEndOfStatement = true;426 return this;427 }428 429 #endregion430 431 #region Properties and Methods432 433 /// <summary>434 /// Gets the collection of commands from this PSCommand435 /// instance.436 /// </summary>437 public CommandCollection Commands438 {439 get440 {441 return _commands;442 }443 }444 445 /// <summary>446 /// The PowerShell instance this PSCommand is associated to, or null if it is an standalone command.447 /// </summary>448 internal PowerShell Owner449 {450 get451 {452 return _owner;453 }454 455 set456 {457 _owner = value;458 }459 }460 461 /// <summary>462 /// Clears the command(s).463 /// </summary>464 public void Clear()465 {466 _commands.Clear();467 _currentCommand = null;468 }469 470 /// <summary>471 /// Creates a shallow copy of the current PSCommand.472 /// </summary>473 /// <returns>474 /// A shallow copy of the current PSCommand475 /// </returns>476 public PSCommand Clone()477 {478 return new PSCommand(this);479 }480 481 #endregion482 483 #region Private Methods484 485 /// <summary>486 /// Initializes the instance. Called from the constructor.487 /// </summary>488 /// <param name="command">489 /// Command to initialize the instance with.490 /// </param>491 /// <param name="isScript">492 /// true if the <paramref name="command"/> is script,493 /// false otherwise.494 /// </param>495 /// <param name="useLocalScope">496 /// if true local scope is used to run the script command.497 /// </param>498 /// <remarks>499 /// Caller should check the input.500 /// </remarks>501 /// <exception cref="ArgumentNullException">502 /// command is null503 /// </exception>504 private void Initialize(string command, bool isScript, bool? useLocalScope)505 {506 _commands = new CommandCollection();507 508 if (command != null)509 {510 _currentCommand = new Command(command, isScript, useLocalScope);511 _commands.Add(_currentCommand);512 }513 }514 515 #endregion516 }517}518 