Wykonaj skrypt programu PowerShell z C # za pomocą argumentów wiersza polecenia

101

Muszę wykonać skrypt PowerShell z poziomu C #. Skrypt potrzebuje argumentów wiersza poleceń.

Oto, co zrobiłem do tej pory:

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

plik scriptFile zawiera coś takiego jak „C: \ Program Files \ MyProgram \ Cokolwiek.ps1”.

Skrypt używa argumentu wiersza poleceń, takiego jak „-key Wartość”, podczas gdy Wartość może być czymś w rodzaju ścieżki, która może również zawierać spacje.

Nie udaje mi się to. Czy ktoś wie, jak przekazać argumenty wiersza polecenia do skryptu PowerShell z poziomu C # i upewnić się, że spacje nie stanowią problemu?

Mephisztoe
źródło
1
Aby wyjaśnić przyszłym użytkownikom, zaakceptowana odpowiedź rozwiązuje problem dla osób mających problemy z przestrzenią nawet bez parametrów użytkowania. Używając:, Command myCommand = new Command(scriptfile);a następnie pipeline.Commands.Add(myCommand);rozwiąż problem ucieczki.
scharette

Odpowiedzi:

111

Spróbuj utworzyć plik skryptu jako osobne polecenie:

Command myCommand = new Command(scriptfile);

następnie możesz dodać parametry za pomocą

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

i w końcu

pipeline.Commands.Add(myCommand);

Oto kompletny, zredagowany kod:

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
źródło
Nadal wydaje mi się, że problem polega na tym, że jeśli wartość jest taka jak c: \ program files \ myprogram, klucz jest ustawiony na c: \ program. :(
Mephisztoe
Nieważne. Czasami pomaga, gdy wiesz, jak poprawnie podzielić ciągi. ;-) Jeszcze raz dziękuję, Twoje rozwiązanie pomogło mi w rozwiązaniu mojego problemu!
Mephisztoe
@Tronex - powinieneś zdefiniować klucz jako parametr twojego skryptu. PowerShell ma kilka świetnych wbudowanych narzędzi do pracy ze ścieżkami. Może zadaj inne pytanie na ten temat. @ Kosi2801 ma poprawną odpowiedź dotyczącą dodawania parametrów.
Steven Murawski
Wpisywanie mojej odpowiedzi nakłada się na Twoją .. Cieszę się, że udało Ci się rozwiązać ten problem!
Steven Murawski
1
Zmienna scriptInvoker nie jest używana.
niaher,
33

Mam inne rozwiązanie. Chcę tylko sprawdzić, czy wykonanie skryptu PowerShell powiedzie się, ponieważ być może ktoś może zmienić politykę. Jako argument podaję tylko ścieżkę skryptu do wykonania.

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

Zawartość skryptu to:

$someVariable = "StringToBeVerifiedInAUnitTest"
$someVariable
Jowen
źródło
1
Cześć. Czy masz pojęcie, dlaczego uruchomienie programu PowerShell zgodnie z opisem i wykonanie procesu wszystkich poleceń (w naszym przypadku) nie kończy się?
Eugeniu Torica
Z jakiej biblioteki korzystasz
SoftwareSavant
11

Miałem problem z przekazaniem parametrów do metody 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

Rozwiązałem to, przekazując nulljako nazwę i parametr jako wartość do kolekcjiCommandParameters

Oto moja funkcja:

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
źródło
Właśnie dodano:using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))...using (Pipeline pipeline = runspace.CreatePipeline())
Red
Kiedy przekazuję wiele parametrów, występuje ten błąd: Nieobsłużony wyjątek typu „System.Management.Automation.ParseException” wystąpił w System.Management.Automation.dll
Muhammad Noman
5

Możesz również po prostu użyć potoku z metodą 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;

Zajmie łańcuch i wszelkie parametry, które mu przekażesz.

James Pogran
źródło
4

Mój jest nieco mniejszy i prostszy:

/// <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
źródło
3

Oto sposób dodawania parametrów do skryptu, jeśli był używany

pipeline.Commands.AddScript(Script);

Dzieje się tak w przypadku użycia HashMap jako parametrów, gdzie klucz jest nazwą zmiennej w skrypcie, a wartością jest wartość zmiennej.

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

A metoda zmiennej wypełnienia to:

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

w ten sposób możesz łatwo dodać wiele parametrów do skryptu. Zauważyłem również, że jeśli chcesz uzyskać wartość ze zmiennej w swoim skrypcie w następujący sposób:

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

// wyniki są nazwą v

będziesz musiał to zrobić tak, jak pokazałem, ponieważ z jakiegoś powodu, jeśli zrobisz to w sposób, w jaki Kosi2801 sugeruje, że lista zmiennych skryptu nie zostanie wypełniona twoimi własnymi zmiennymi.

Ruud
źródło
3

Dla mnie najbardziej elastycznym sposobem uruchamiania skryptu PowerShell z C # było użycie PowerShell.Create().AddScript()

Fragment kodu to

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

Możesz sprawdzić, czy wystąpił jakiś błąd, sprawdzając Streams.Error. Warto było sprawdzić kolekcję. Użytkownik to typ obiektu, który zwraca skrypt programu PowerShell.

Andy
źródło