Führen Sie PowerShell Script aus C # mit Befehlszeilenargumenten aus

101

Ich muss ein PowerShell-Skript in C # ausführen. Das Skript benötigt Befehlszeilenargumente.

Folgendes habe ich bisher getan:

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 enthält so etwas wie "C: \ Programme \ MyProgram \ Whatever.ps1".

Das Skript verwendet ein Befehlszeilenargument wie "-key Value", während Value so etwas wie ein Pfad sein kann, der auch Leerzeichen enthalten kann.

Ich bekomme das nicht zum Laufen. Weiß jemand, wie man Befehlszeilenargumente aus C # heraus an ein PowerShell-Skript übergibt und sicherstellt, dass Leerzeichen kein Problem sind?

Mephisztoe
quelle
1
Nur um dies für zukünftige Benutzer zu klären, löst eine akzeptierte Antwort das Problem für Personen, die Probleme mit Leerzeichen haben, auch ohne die Verwendungsparameter. Verwenden von: Command myCommand = new Command(scriptfile);und pipeline.Commands.Add(myCommand);lösen Sie dann das Escape-Problem.
scharette

Antworten:

111

Versuchen Sie, eine Skriptdatei als separaten Befehl zu erstellen:

Command myCommand = new Command(scriptfile);

dann können Sie mit Parameter hinzufügen

CommandParameter testParam = new CommandParameter("key","value");
myCommand.Parameters.Add(testParam);

und schlussendlich

pipeline.Commands.Add(myCommand);

Hier ist der vollständige, bearbeitete 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();
Kosi2801
quelle
Ich habe immer noch das Problem, dass der Schlüssel auf c: \ program gesetzt ist, wenn der Wert so etwas wie c: \ program files \ myprogram ist. :(
Mephisztoe
Keine Ursache. Manchmal hilft es, wenn Sie wissen, wie man Strings richtig teilt. ;-) Nochmals vielen Dank, Ihre Lösung hat mir bei der Lösung meines Problems geholfen!
Mephisztoe
@Tronex - Sie sollten den Schlüssel als Parameter für Ihr Skript definieren. PowerShell verfügt über einige großartige integrierte Tools zum Arbeiten mit Pfaden. Vielleicht eine andere Frage dazu stellen. @ Kosi2801 hat die richtige Antwort zum Hinzufügen von Parametern.
Steven Murawski
Das Schreiben meiner Antwort überlappte Ihre. Ich bin froh, dass Sie es gelöst haben!
Steven Murawski
1
Die Variable scriptInvoker wird nicht verwendet.
Niaher
33

Ich habe eine andere Lösung. Ich möchte nur testen, ob die Ausführung eines PowerShell-Skripts erfolgreich ist, da möglicherweise jemand die Richtlinie ändert. Als Argument gebe ich nur den Pfad des auszuführenden Skripts an.

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));

Mit dem Inhalt des Skripts:

$someVariable = "StringToBeVerifiedInAUnitTest"
$someVariable
Jowen
quelle
1
Hallo. Haben Sie eine Idee, warum das Starten von Powershell wie beschrieben und das Ausführen aller Befehle (in unserem Fall) nicht beendet wird?
Eugeniu Torica
Welche Bibliothek verwenden Sie
SoftwareSavant
11

Ich hatte Probleme beim Übergeben von Parametern an die Commands.AddScript-Methode.

C:\Foo1.PS1 Hello World Hunger
C:\Foo2.PS1 Hello World

scriptFile = "C:\Foo1.PS1"

parameters = "parm1 parm2 parm3" ... variable length of params

Ich habe dieses nullProblem behoben, indem ich den Namen und den Parameter als Wert in eine Sammlung von übergeben habeCommandParameters

Hier ist meine Funktion:

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<CommandParameter> commandParameters = new Collection<CommandParameter>();
    foreach (string scriptParameter in scriptParameters.Split(' '))
    {
        CommandParameter commandParm = new CommandParameter(null, scriptParameter);
        commandParameters.Add(commandParm);
        scriptCommand.Parameters.Add(commandParm);
    }
    pipeline.Commands.Add(scriptCommand);
    Collection<PSObject> psObjects;
    psObjects = pipeline.Invoke();
}
Twalker
quelle
Gerade hinzugefügt:using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))...using (Pipeline pipeline = runspace.CreatePipeline())
Red
Wenn ich mehrere Parameter übergebe, tritt der folgende Fehler auf: Eine nicht behandelte Ausnahme vom Typ 'System.Management.Automation.ParseException' ist in System.Management.Automation.dll
Muhammad Noman
5

Sie können die Pipeline auch einfach mit der AddScript-Methode verwenden:

string cmdArg = ".\script.ps1 -foo bar"            
Collection<PSObject> psresults;
using (Pipeline pipeline = _runspace.CreatePipeline())
            {
                pipeline.Commands.AddScript(cmdArg);
                pipeline.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
                psresults = pipeline.Invoke();
            }
return psresults;

Es wird eine Zeichenfolge und alle Parameter benötigt, die Sie übergeben.

James Pogran
quelle
4

Meins ist etwas kleiner und einfacher:

/// <summary>
/// Runs a PowerShell script taking it's path and parameters.
/// </summary>
/// <param name="scriptFullPath">The full file path for the .ps1 file.</param>
/// <param name="parameters">The parameters for the script, can be null.</param>
/// <returns>The output from the PowerShell execution.</returns>
public static ICollection<PSObject> RunScript(string scriptFullPath, ICollection<CommandParameter> 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;
}
Pedro Luz
quelle
3

Hier ist eine Möglichkeit, dem Skript Parameter hinzuzufügen, wenn Sie diese verwendet haben

pipeline.Commands.AddScript(Script);

Bei Verwendung einer HashMap als Parameter ist der Schlüssel der Name der Variablen im Skript und der Wert der Wert der Variablen.

pipeline.Commands.AddScript(script));
FillVariables(pipeline, scriptParameter);
Collection<PSObject> results = pipeline.Invoke();

Und die Füllvariablenmethode ist:

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);
    }
  }
}

Auf diese Weise können Sie einem Skript problemlos mehrere Parameter hinzufügen. Ich habe auch festgestellt, dass, wenn Sie einen Wert aus einer Variablen in Ihrem Skript erhalten möchten, wie folgt:

Object resultcollection = runspace.SessionStateProxy.GetVariable("results");

// Ergebnisse sind der Name des v

Sie müssen es so machen, wie ich es gezeigt habe, denn aus irgendeinem Grund, wenn Sie es so machen, wie Kosi2801 vorschlägt, wird die Liste der Skriptvariablen nicht mit Ihren eigenen Variablen gefüllt.

Ruud
quelle
3

Für mich war die flexibelste Möglichkeit, PowerShell-Skripte über C # auszuführen, die Verwendung PowerShell.Create().AddScript()

Das Snippet des Codes ist

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<User>();
foreach (var errorRecord in _powershell.Streams.Error)
    Console.WriteLine(errorRecord);

Sie können überprüfen, ob ein Fehler vorliegt, indem Sie Streams.Error überprüfen. Es war sehr praktisch, die Sammlung zu überprüfen. Benutzer ist der Objekttyp, den das PowerShell-Skript zurückgibt.

Andy
quelle