Выполнение сценария PowerShell из C # с аргументами командной строки

102

Мне нужно выполнить сценарий PowerShell из C #. Сценарию нужны аргументы командной строки.

Вот что я сделал до сих пор:

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 содержит что-то вроде «C: \ Program Files \ MyProgram \ Whatever.ps1».

Сценарий использует аргумент командной строки, такой как «-key Value», тогда как Value может быть чем-то вроде пути, который также может содержать пробелы.

Я не могу заставить это работать. Кто-нибудь знает, как передать аргументы командной строки сценарию PowerShell из C # и убедиться, что пробелы не являются проблемой?

Mephisztoe
источник
1
Просто чтобы прояснить для будущих пользователей, принятый ответ решает проблему для людей, у которых есть проблемы с пробелами даже без параметров использования. Использование: Command myCommand = new Command(scriptfile);а затем pipeline.Commands.Add(myCommand);решить проблему с побегом.
scharette

Ответы:

112

Попробуйте создать файл сценария как отдельную команду:

Command myCommand = new Command(scriptfile);

тогда вы можете добавить параметры с помощью

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

и наконец

pipeline.Commands.Add(myCommand);

Вот полный отредактированный код:

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();
Коси2801
источник
Кажется, у меня все еще есть проблема, что если значение похоже на c: \ program files \ myprogram, ключ установлен на c: \ program. :(
Mephisztoe
1
Неважно. Иногда помогает, когда вы знаете, как правильно разделить струны. ;-) Еще раз спасибо, ваше решение помогло мне в решении моей проблемы!
Mephisztoe
@Tronex - вы должны определить ключ как параметр для вашего скрипта. PowerShell имеет несколько отличных встроенных инструментов для работы с путями. Может быть, задать еще один вопрос по этому поводу. @ Kosi2801 имеет правильный ответ для добавления параметров.
Стивен Муравски,
Мой ответ накладывался на ваш ... Я рад, что вы решили проблему!
Стивен Муравски,
2
Переменная scriptInvoker не используется.
niaher
33

У меня есть другое решение. Я просто хочу проверить, успешно ли выполняется сценарий PowerShell, потому что, возможно, кто-то может изменить политику. В качестве аргумента я просто указываю путь исполняемого скрипта.

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

С содержимым сценария:

$someVariable = "StringToBeVerifiedInAUnitTest"
$someVariable
Jowen
источник
1
Здравствуй. У вас есть идеи, почему запуск PowerShell, как вы описали, и выполнение всех команд (в нашем случае) не завершается?
Eugeniu Torica
Какую библиотеку вы используете
SoftwareSavant
11

У меня возникли проблемы с передачей параметров в метод 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

Я решил эту проблему, передав nullкак имя и параметр как значение в коллекциюCommandParameters

Вот моя функция:

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();
}
тукер
источник
Только что добавлено:using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))...using (Pipeline pipeline = runspace.CreatePipeline())
Red
Когда я передаю несколько параметров, возникает следующая ошибка: необработанное исключение типа System.Management.Automation.ParseException произошло в System.Management.Automation.dll
Мухаммад Номан
5

Вы также можете просто использовать конвейер с методом 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;

Потребуется строка и любые параметры, которые вы ей передадите.

Джеймс Погран
источник
4

Мой немного меньше и проще:

/// <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;
}
Педро Луз
источник
3

Вот способ добавить параметры в скрипт, если вы использовали

pipeline.Commands.AddScript(Script);

Это с использованием HashMap в качестве параметров, ключ - это имя переменной в скрипте, а значение - это значение переменной.

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

И метод переменной заполнения:

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

таким образом вы можете легко добавить в сценарий несколько параметров. Я также заметил, что если вы хотите получить значение из переменной в своем скрипте, например:

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

// результат - это имя v

вам придется делать это так, как я показал, потому что по какой-то причине, если вы сделаете это так, как Kosi2801 предлагает, список переменных скрипта не будет заполнен вашими собственными переменными.

Рууд
источник
3

Для меня наиболее гибким способом запуска сценария PowerShell из C # было использование PowerShell.Create().AddScript()

Фрагмент кода

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

Вы можете проверить наличие ошибки, проверив Streams.Error. Проверить коллекцию было действительно удобно. Пользователь - это тип объекта, который возвращает сценарий PowerShell.

Энди
источник