Exécuter le script PowerShell à partir de C # avec des arguments de ligne de commande

101

J'ai besoin d'exécuter un script PowerShell à partir de C #. Le script a besoin d'arguments de ligne de commande.

Voici ce que j'ai fait jusqu'à présent:

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 contient quelque chose comme "C: \ Program Files \ MyProgram \ Whatever.ps1".

Le script utilise un argument de ligne de commande tel que "-key Value" alors que Value peut être quelque chose comme un chemin qui peut également contenir des espaces.

Je ne fais pas fonctionner ça. Quelqu'un sait-il comment transmettre des arguments de ligne de commande à un script PowerShell à partir de C # et s'assurer que les espaces ne posent aucun problème?

Mephisztoe
la source
1
Juste pour clarifier pour les futurs utilisateurs, la réponse acceptée résout le problème pour les personnes ayant des problèmes avec les espaces même sans les paramètres d'utilisation. À l'aide de: Command myCommand = new Command(scriptfile);puis pipeline.Commands.Add(myCommand);résolvez le problème d'échappement.
scharette

Réponses:

111

Essayez de créer un fichier script en tant que commande distincte:

Command myCommand = new Command(scriptfile);

alors vous pouvez ajouter des paramètres avec

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

et enfin

pipeline.Commands.Add(myCommand);

Voici le code complet et édité:

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
la source
Je semble toujours avoir le problème que si la valeur est quelque chose comme c: \ program files \ myprogram, la clé est définie sur c: \ program. :(
Mephisztoe
Ça ne fait rien. Parfois, cela aide lorsque vous savez comment séparer correctement les chaînes. ;-) Merci encore, votre solution m'a aidé à résoudre mon problème!
Mephisztoe
@Tronex - vous devriez définir la clé comme étant un paramètre pour votre script. PowerShell dispose d'excellents outils intégrés pour travailler avec les chemins. Posez peut-être une autre question à ce sujet. @ Kosi2801 a la bonne réponse pour l'ajout de paramètres.
Steven Murawski le
Taper ma réponse chevauchait la vôtre .. Je suis content que vous ayez résolu le problème!
Steven Murawski le
1
La variable scriptInvoker n'est pas utilisée.
niaher
33

J'ai une autre solution. Je veux juste tester si l'exécution d'un script PowerShell réussit, car peut-être que quelqu'un pourrait changer la politique. Comme argument, je spécifie simplement le chemin du script à exécuter.

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

Le contenu du script étant:

$someVariable = "StringToBeVerifiedInAUnitTest"
$someVariable
Jowen
la source
1
Salut. Avez-vous une idée de la raison pour laquelle démarrer PowerShell comme vous l'avez décrit et exécuter tous les processus de commandes (dans notre cas) ne se termine pas?
Eugeniu Torica
Quelle bibliothèque utilisez-vous
SoftwareSavant
11

J'ai eu du mal à passer des paramètres à la méthode Commands.AddScript.

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

scriptFile = "C:\Foo1.PS1"

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

J'ai résolu ce problème en passant nullle nom et le paramètre comme valeur dans une collection deCommandParameters

Voici ma fonction:

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
la source
Vient d'ajouter:using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))...using (Pipeline pipeline = runspace.CreatePipeline())
Rouge
Lorsque je passe plusieurs paramètres, cette erreur se produit: Une exception non gérée de type «System.Management.Automation.ParseException» s'est produite dans System.Management.Automation.dll
Muhammad Noman
5

Vous pouvez également simplement utiliser le pipeline avec la méthode AddScript:

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;

Cela prendra une chaîne et quels que soient les paramètres que vous lui transmettez.

James Pogran
la source
4

Le mien est un peu plus petit et plus simple:

/// <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
la source
3

Voici un moyen d'ajouter des paramètres au script si vous avez utilisé

pipeline.Commands.AddScript(Script);

C'est avec l'utilisation d'un HashMap comme paramètres que la clé est le nom de la variable dans le script et la valeur est la valeur de la variable.

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

Et la méthode de la variable de remplissage est:

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

de cette façon, vous pouvez facilement ajouter plusieurs paramètres à un script. J'ai également remarqué que si vous souhaitez obtenir une valeur d'une variable dans votre script comme ceci:

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

// résultats étant le nom du v

vous devrez le faire comme je l'ai montré car pour une raison quelconque, si vous le faites comme Kosi2801 suggère que la liste des variables de script ne soit pas remplie avec vos propres variables.

Ruud
la source
3

Pour moi, le moyen le plus flexible d'exécuter un script PowerShell à partir de C # était d'utiliser PowerShell.Create().AddScript()

L'extrait de code est

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

Vous pouvez vérifier s'il y a une erreur en vérifiant Streams.Error. C'était vraiment pratique de vérifier la collection. L'utilisateur est le type d'objet renvoyé par le script PowerShell.

Andy
la source