Ejecute el script de PowerShell desde C # con argumentos de línea de comandos

101

Necesito ejecutar un script de PowerShell desde C #. El script necesita argumentos de línea de comandos.

Esto es lo que he hecho hasta ahora:

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 contiene algo como "C: \ Archivos de programa \ MyProgram \ Whatever.ps1".

El script usa un argumento de línea de comando como "-key Value" mientras que Value puede ser algo así como una ruta que también puede contener espacios.

No consigo que esto funcione. ¿Alguien sabe cómo pasar argumentos de línea de comando a un script de PowerShell desde C # y asegurarse de que los espacios no sean un problema?

Mephisztoe
fuente
1
Solo para aclarar a los futuros usuarios, la respuesta aceptada resuelve el problema para las personas que tienen problemas con los espacios incluso sin los parámetros de uso. Usando: Command myCommand = new Command(scriptfile);y luego pipeline.Commands.Add(myCommand);resuelva el problema de escape.
scharette

Respuestas:

111

Intente crear scriptfile como un comando separado:

Command myCommand = new Command(scriptfile);

entonces puedes agregar parámetros con

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

y finalmente

pipeline.Commands.Add(myCommand);

Aquí está el código completo y editado:

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
fuente
Parece que todavía tengo el problema de que si el valor es algo como c: \ program files \ myprogram, la clave está establecida en c: \ program. :(
Mephisztoe
No importa. A veces resulta útil saber cómo dividir correctamente las cuerdas. ;-) Gracias de nuevo, ¡su solución me ayudó a resolver mi problema!
Mephisztoe
@Tronex: debe definir la clave como un parámetro para su script. PowerShell tiene excelentes herramientas integradas para trabajar con rutas. Tal vez haga otra pregunta sobre eso. @ Kosi2801 tiene la respuesta correcta para agregar parámetros.
Steven Murawski
Al escribir mi respuesta se superpuso a la tuya ... ¡Me alegra que lo hayas resuelto!
Steven Murawski
1
La variable scriptInvoker no se está utilizando.
niaher
33

Tengo otra solucion Solo quiero probar si la ejecución de un script de PowerShell tiene éxito, porque quizás alguien podría cambiar la política. Como argumento, solo especifico la ruta del script a ejecutar.

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

Siendo el contenido del guión:

$someVariable = "StringToBeVerifiedInAUnitTest"
$someVariable
Jowen
fuente
1
Hola. ¿Tiene alguna idea de por qué iniciar powershell como lo describió y ejecutar todos los procesos de comandos (en nuestro caso) no sale?
Eugeniu Torica
¿Qué biblioteca estás usando
SoftwareSavant
11

Tuve problemas para pasar parámetros al método 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

Resolví esto pasando nullcomo el nombre y el parámetro como valor en una colección deCommandParameters

Aquí está mi función:

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();
}
pendejo
fuente
Recién añadido:using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))...using (Pipeline pipeline = runspace.CreatePipeline())
Rojo
Cuando paso varios parámetros, se produce este error: se produjo una excepción no controlada del tipo 'System.Management.Automation.ParseException' en System.Management.Automation.dll
Muhammad Noman
5

También puede usar la canalización con el método 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;

Tomará una cadena y cualquier parámetro que le pase.

James Pogran
fuente
4

El mío es un poco más pequeño y 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
fuente
3

Aquí hay una forma de agregar parámetros al script si usó

pipeline.Commands.AddScript(Script);

Esto es con el uso de un HashMap como parámetros, la clave es el nombre de la variable en el script y el valor es el valor de la variable.

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

Y el método de variable de relleno es:

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 esta manera puede agregar fácilmente múltiples parámetros a un script. También he notado que si desea obtener un valor de una variable en su secuencia de comandos así:

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

// resulta el nombre de la v

tendrá que hacerlo de la forma que le mostré porque, por alguna razón, si lo hace de la forma en que Kosi2801 sugiere que la lista de variables de script no se llena con sus propias variables.

Ruud
fuente
3

Para mí, la forma más flexible de ejecutar el script de PowerShell desde C # era usar PowerShell.Create().AddScript()

El fragmento del código es

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

Puede comprobar si hay algún error marcando Streams.Error. Fue muy útil comprobar la colección. Usuario es el tipo de objeto que devuelve el script de PowerShell.

Andy
fuente