Execute PowerShell Script em C # com argumentos de linha de comando

101

Preciso executar um script PowerShell de dentro do C #. O script precisa de argumentos de linha de comando.

Isso é o que eu fiz até agora:

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 contém algo como "C: \ Arquivos de programas \ MyProgram \ Whatever.ps1".

O script usa um argumento de linha de comando como "-key Value", enquanto Value pode ser algo como um caminho que também pode conter espaços.

Eu não consigo fazer isso funcionar. Alguém sabe como passar argumentos de linha de comando para um script do PowerShell de dentro do C # e certificar-se de que os espaços não são problema?

Mephisztoe
fonte
1
Só para esclarecer para futuros usuários, a resposta aceita resolve o problema para quem tem problemas com espaços mesmo sem os parâmetros de uso. Usando: Command myCommand = new Command(scriptfile);e, em seguida, pipeline.Commands.Add(myCommand);resolva o problema de escape.
scharette

Respostas:

111

Tente criar scriptfile como um comando separado:

Command myCommand = new Command(scriptfile);

então você pode adicionar parâmetros com

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

e finalmente

pipeline.Commands.Add(myCommand);

Aqui está o código editado completo:

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
fonte
Ainda pareço ter o problema de que se o valor for algo como c: \ arquivos de programas \ meuprograma, a chave é definida como c: \ programa. :(
Mephisztoe
Deixa pra lá. Às vezes ajuda quando você sabe como dividir as strings corretamente. ;-) Obrigado novamente, sua solução me ajudou a resolver meu problema!
Mephisztoe
@Tronex - você deve definir a chave como um parâmetro para seu script. O PowerShell tem algumas ótimas ferramentas internas para trabalhar com caminhos. Talvez faça outra pergunta sobre isso. @ Kosi2801 tem a resposta correta para adicionar parâmetros.
Steven Murawski
Ao digitar minha resposta sobrepôs a sua. Fico feliz que você tenha resolvido!
Steven Murawski
1
A variável scriptInvoker não está sendo usada.
niaher de
33

Eu tenho outra solução. Só quero testar se a execução de um script do PowerShell é bem-sucedida, porque talvez alguém possa alterar a política. Como argumento, apenas especifico o caminho do script a ser executado.

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

Com o conteúdo do script sendo:

$someVariable = "StringToBeVerifiedInAUnitTest"
$someVariable
Jowen
fonte
1
Oi. Você tem alguma ideia por que iniciar o PowerShell como você descreveu e executar todos os processos de comandos (no nosso caso) não termina?
Eugeniu Torica
Que biblioteca você está usando
SoftwareSavant
11

Tive problemas para passar parâmetros para o 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

Resolvi isso passando nullcomo o nome e o parâmetro como valor em uma coleção deCommandParameters

Aqui está minha função:

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
fonte
Acabei de adicionar:using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))...using (Pipeline pipeline = runspace.CreatePipeline())
Vermelho
Quando estou passando vários parâmetros, ocorre este erro: Ocorreu uma exceção não tratada do tipo 'System.Management.Automation.ParseException' em System.Management.Automation.dll
Muhammad Noman
5

Você também pode apenas usar o pipeline com o 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;

Ele terá uma string e quaisquer parâmetros que você passar.

James Pogran
fonte
4

O meu é um pouco mais pequeno e simples:

/// <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
fonte
3

Esta é uma maneira de adicionar parâmetros ao script se você usou

pipeline.Commands.AddScript(Script);

Isso ocorre com o uso de um HashMap como parâmetros, a chave sendo o nome da variável no script e o valor é o valor da variável.

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

E o método da variável de preenchimento é:

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

dessa forma, você pode adicionar facilmente vários parâmetros a um script. Também percebi que se você deseja obter um valor de uma variável em seu script, como:

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

// resultados sendo o nome de v

você terá que fazer da maneira que mostrei porque, por algum motivo, se você fizer da maneira que Kosi2801 sugere, a lista de variáveis ​​de script não é preenchida com suas próprias variáveis.

Ruud
fonte
3

Para mim, a maneira mais flexível de executar o script do PowerShell em C # era usando PowerShell.Create().AddScript()

O snippet do código é

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

Você pode verificar se há algum erro verificando Streams.Error. Foi muito útil conferir a coleção. Usuário é o tipo de objeto que o script do PowerShell retorna.

Andy
fonte