3

Why is performance of executing a script from PowerShell directly much quicker than when executing the script from the System.Management.Automation.PowerShell class? When I execute my script directly in PS, it takes less than a second, though when I execute it through the code below, the Invoke takes several minutes.

public bool Execute(string filename, out string result)
{
    StringBuilder sb = new StringBuilder();

    using (PowerShell ps = PowerShell.Create())
    {
        ps.Runspace.SessionStateProxy.SetVariable("filename", filename);
        ps.AddScript(this.script);
        Collection<PSObject> psOutput = ps.Invoke();

        foreach (PSObject item in psOutput)
        {
            Console.WriteLine(item);
        }

        PSDataCollection<ErrorRecord> errors = ps.Streams.Error;
        foreach (ErrorRecord err in errors)
        {
            Console.WriteLine(err);
        }

        result = sb.ToString();
        return errors.Count == 0;
    }
}

Script text:

[regex]$r = "\$\%\$@@(.+)\$\%\$@@";

(Get-Content $filename) | 
    Foreach-Object {
        $line = $_;
        $find = $r.matches($line);

        if ($find[0].Success) {
            foreach ($match in $find) {
                $found = $match.value           
                $replace = $found -replace "[^A-Za-z0-9\s]", "";            
                $line.Replace($found, $replace);
            }       
        }
        else 
        {
            $line;
        }
    } |
Set-Content $filename

I tested the execution of the script against this method in c#, and this method takes less than 2 seconds to execute the script.

public bool Execute(string filename, out string result)
{
    StringBuilder standardOutput = new StringBuilder();
    string currentPath = Path.GetDirectoryName(filename);
    string psFile = Path.Combine(currentPath, Path.GetFileNameWithoutExtension(Path.GetRandomFileName()) + ".ps1");

    this.script = this.script.Replace("$filename", "\"" + filename + "\"");
    File.WriteAllText(psFile, this.script);

    using (Process process = new Process())
    {
        process.StartInfo = new ProcessStartInfo("powershell.exe", String.Format("-executionpolicy unrestricted \"{0}\"", psFile))
        {
            WindowStyle = ProcessWindowStyle.Hidden,
            UseShellExecute = false,
            RedirectStandardOutput = true
        };

        process.Start();

        var output = process.StandardOutput.ReadToEnd();
        while (!process.HasExited)
        {
            standardOutput.Append(process.StandardOutput.ReadToEnd());
        }

        process.WaitForExit();
        standardOutput.Append(process.StandardOutput.ReadToEnd());

        result = standardOutput.ToString();
        return process.ExitCode == 0;
    }
}
mservidio
  • 12,817
  • 9
  • 58
  • 84
  • Have you timed just the Execute method you've shown here? If not, it's possible it is another part of your program that is taking so long. Also, can you post your script? Perhaps there is something that is causing the extra time when it is run in your C# use of the PowerShell engine. Perhaps some profile difference? – Keith Hill Sep 26 '14 at 00:14
  • @KeithHill-I've added the script, and another method of executing the script which executes the script against the same filename in less than 2 seconds. – mservidio Sep 26 '14 at 00:25
  • I tested with a Copy-Item, and that command runs quickly within the script through both methods of execution. Something about the Foreach-Object or RegEx logic that the PowerShell Automation class seems not to like too much. – mservidio Sep 26 '14 at 00:45
  • 1
    It's taking about 44 seconds to write 1MB of data to the file through the Powershell.Invoke method. – mservidio Sep 26 '14 at 00:51
  • 1
    You need to be careful with double quoted regex patterns in PowerShell. This pattern `"\$\%\$@@(.+)\$\%\$@@";` has some characters that PowerShell will try to interpolate before the string hits the regex. Use a single quoted string instead `'\$\%\$@@(.+)\$\%\$@@'` – Keith Hill Sep 26 '14 at 02:37
  • @KeithHill-I tried changing to single quotes from double quotes, but the performance through Powershell.Invoke is still just as slow. – mservidio Sep 26 '14 at 15:33

0 Answers0