可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need to execute a PowerShell script from within C#. The script needs commandline arguments.
This is what I have done so far:
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create(); Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration); runspace.Open(); RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace); Pipeline pipeline = runspace.CreatePipeline(); pipeline.Commands.Add(scriptFile); // Execute PowerShell script results = pipeline.Invoke();
scriptFile contains something like "C:\Program Files\MyProgram\Whatever.ps1".
The script uses a commandline argument such as "-key Value" whereas Value can be something like a path that also might contain spaces.
I don't get this to work. Does anyone know how to pass commandline arguments to a PowerShell script from within C# and make sure that spaces are no problem?
回答1:
Try creating scriptfile as a separate command:
Command myCommand = new Command(scriptfile);
then you can add parameters with
CommandParameter testParam = new CommandParameter("key","value"); myCommand.Parameters.Add(testParam);
and finally
pipeline.Commands.Add(myCommand);
Here is the complete, edited code:
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create(); Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration); runspace.Open(); RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace); Pipeline pipeline = runspace.CreatePipeline(); //Here's how you add a new script with arguments Command myCommand = new Command(scriptfile); CommandParameter testParam = new CommandParameter("key","value"); myCommand.Parameters.Add(testParam); pipeline.Commands.Add(myCommand); // Execute PowerShell script results = pipeline.Invoke();
回答2:
I have another solution. I just want to test if executing a PowerShell script succeeds, because perhaps somebody might change the policy. As the argument, I just specify the path of the script to be executed.
ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = @"powershell.exe"; startInfo.Arguments = @"& 'c:\Scripts\test.ps1'"; startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardError = true; startInfo.UseShellExecute = false; startInfo.CreateNoWindow = true; Process process = new Process(); process.StartInfo = startInfo; process.Start(); string output = process.StandardOutput.ReadToEnd(); Assert.IsTrue(output.Contains("StringToBeVerifiedInAUnitTest")); string errors = process.StandardError.ReadToEnd(); Assert.IsTrue(string.IsNullOrEmpty(errors));
With the contents of the script being:
$someVariable = "StringToBeVerifiedInAUnitTest" $someVariable
回答3:
Any chance I could get more clarity on the passing params to the Commands.AddScript method?
C:\Foo1.PS1 Hello World Hunger C:\Foo2.PS1 Hello World
scriptFile = "C:\Foo1.PS1"
parameters = "parm1 parm2 parm3" ... variable length of params
Resolved this ... passing null as the name and the param as value into a collection of CommandParameters
Here is my function:
private static void RunPowershellScript(string scriptFile, string scriptParameters) { RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create(); Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration); runspace.Open(); RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace); Pipeline pipeline = runspace.CreatePipeline(); Command scriptCommand = new Command(scriptFile); Collection commandParameters = new Collection(); foreach (string scriptParameter in scriptParameters.Split(' ')) { CommandParameter commandParm = new CommandParameter(null, scriptParameter); commandParameters.Add(commandParm); scriptCommand.Parameters.Add(commandParm); } pipeline.Commands.Add(scriptCommand); Collection psObjects; psObjects = pipeline.Invoke(); }
回答4:
You can also just use the pipeline with the AddScript Method:
string cmdArg = ".\script.ps1 -foo bar" Collection psresults; using (Pipeline pipeline = _runspace.CreatePipeline()) { pipeline.Commands.AddScript(cmdArg); pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output); psresults = pipeline.Invoke(); } return psresults;
It will take a string, and whatever parameters you pass it.
回答5:
Here is a way to add Parameters to the script if you used
pipeline.Commands.AddScript(Script);
This is with using an HashMap as paramaters the key being the name of the variable in the script and the value is the value of the variable.
pipeline.Commands.AddScript(script)); FillVariables(pipeline, scriptParameter); Collection results = pipeline.Invoke();
And the fill variable method is:
private static void FillVariables(Pipeline pipeline, Hashtable scriptParameters) { // Add additional variables to PowerShell if (scriptParameters != null) { foreach (DictionaryEntry entry in scriptParameters) { CommandParameter Param = new CommandParameter(entry.Key as String, entry.Value); pipeline.Commands[0].Parameters.Add(Param); } } }
this way you can easily add multiple parameters to a script. I've also noticed that if you want to get a value from a variable in you script like so:
Object resultcollection = runspace.SessionStateProxy.GetVariable("results");
//results being the name of the v
you'll have to do it the way I showed because for some reason if you do it the way Kosi2801 suggests the script variables list doesn't get filled with your own variables.
回答6:
Mine is a bit more smaller and simpler:
/// /// Runs a PowerShell script taking it's path and parameters. /// /// The full file path for the .ps1 file. /// The parameters for the script, can be null. /// The output from the PowerShell execution. public static ICollection RunScript(string scriptFullPath, ICollection parameters = null) { var runspace = RunspaceFactory.CreateRunspace(); runspace.Open(); var pipeline = runspace.CreatePipeline(); var cmd = new Command(scriptFullPath); if (parameters != null) { foreach (var p in parameters) { cmd.Parameters.Add(p); } } pipeline.Commands.Add(cmd); var results = pipeline.Invoke(); pipeline.Dispose(); runspace.Dispose(); return results; }
回答7:
For me, the most flexible way to run PowerShell script from C# was using PowerShell.Create().AddScript()
The snippet of the code is
string scriptDirectory = Path.GetDirectoryName( ConfigurationManager.AppSettings["PathToTechOpsTooling"]); var script = "Set-Location " + scriptDirectory + Environment.NewLine + "Import-Module .\\script.psd1" + Environment.NewLine + "$data = Import-Csv -Path " + tempCsvFile + " -Encoding UTF8" + Environment.NewLine + "New-Registration -server " + dbServer + " -DBName " + dbName + " -Username \"" + user.Username + "\" + -Users $userData"; _powershell = PowerShell.Create().AddScript(script); _powershell.Invoke(); foreach (var errorRecord in _powershell.Streams.Error) Console.WriteLine(errorRecord);
You can check if there's any error by checking Streams.Error. It was really handy to check the collection. User is the type of object the PowerShell script returns.