-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRunCommand.cs
More file actions
147 lines (127 loc) · 5.61 KB
/
RunCommand.cs
File metadata and controls
147 lines (127 loc) · 5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright (c) ktsu.dev
// All rights reserved.
// Licensed under the MIT license.
namespace ktsu.RunCommand;
using System.Diagnostics;
using System.Runtime.InteropServices;
/// <summary>
/// Provides functionality to execute shell commands and process their outputs.
/// </summary>
public static class RunCommand
{
/// <summary>
/// Executes a shell command synchronously.
/// </summary>
/// <param name="command">The command to execute.</param>
/// <returns>The exit code of the executed process.</returns>
public static int Execute(string command) =>
ExecuteAsync(command).Result;
/// <summary>
/// Executes a shell command synchronously with an output handler.
/// </summary>
/// <param name="command">The command to execute.</param>
/// <param name="outputHandler">The handler for processing command output.</param>
/// <returns>The exit code of the executed process.</returns>
public static int Execute(string command, OutputHandler outputHandler) =>
ExecuteAsync(command, outputHandler).Result;
/// <summary>
/// Executes a shell command synchronously at the specified elevation level.
/// </summary>
/// <param name="command">The command to execute.</param>
/// <param name="elevation">The privilege level under which to run the command.</param>
/// <returns>The exit code of the executed process.</returns>
public static int Execute(string command, Elevation elevation) =>
ExecuteAsync(command, elevation).Result;
/// <summary>
/// Executes a shell command synchronously with an output handler at the specified elevation level.
/// </summary>
/// <param name="command">The command to execute.</param>
/// <param name="outputHandler">
/// The handler for processing command output. Not invoked when <paramref name="elevation"/> is
/// <see cref="Elevation.Elevated"/> on Windows because elevation requires <c>UseShellExecute</c>,
/// which is incompatible with output redirection.
/// </param>
/// <param name="elevation">The privilege level under which to run the command.</param>
/// <returns>The exit code of the executed process.</returns>
public static int Execute(string command, OutputHandler outputHandler, Elevation elevation) =>
ExecuteAsync(command, outputHandler, elevation).Result;
/// <summary>
/// Executes a shell command asynchronously
/// </summary>
/// <param name="command">The command to execute.</param>
/// <returns>A task representing the asynchronous operation with the process exit code.</returns>
public static async Task<int> ExecuteAsync(string command)
=> await ExecuteAsync(command, new OutputHandler()).ConfigureAwait(false);
/// <summary>
/// Executes a shell command asynchronously with an output handler.
/// </summary>
/// <param name="command">The command to execute.</param>
/// <param name="outputHandler">The handler for processing command output.</param>
/// <returns>A task representing the asynchronous operation with the process exit code.</returns>
public static async Task<int> ExecuteAsync(string command, OutputHandler outputHandler)
=> await ExecuteAsync(command, outputHandler, Elevation.Default).ConfigureAwait(false);
/// <summary>
/// Executes a shell command asynchronously at the specified elevation level.
/// </summary>
/// <param name="command">The command to execute.</param>
/// <param name="elevation">The privilege level under which to run the command.</param>
/// <returns>A task representing the asynchronous operation with the process exit code.</returns>
public static async Task<int> ExecuteAsync(string command, Elevation elevation)
=> await ExecuteAsync(command, new(), elevation).ConfigureAwait(false);
/// <summary>
/// Executes a shell command asynchronously with an output handler at the specified elevation level.
/// </summary>
/// <param name="command">The command to execute.</param>
/// <param name="outputHandler">
/// The handler for processing command output. Not invoked when <paramref name="elevation"/> is
/// <see cref="Elevation.Elevated"/> on Windows because elevation requires <c>UseShellExecute</c>,
/// which is incompatible with output redirection.
/// </param>
/// <param name="elevation">The privilege level under which to run the command.</param>
/// <returns>A task representing the asynchronous operation with the process exit code.</returns>
public static async Task<int> ExecuteAsync(string command, OutputHandler outputHandler, Elevation elevation)
{
Ensure.NotNull(command);
Ensure.NotNull(outputHandler);
string[] commandParts = command.Split(' ', 2);
string filename = commandParts[0];
string arguments = commandParts.Length > 1 ? commandParts[1] : string.Empty;
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
bool useElevation = elevation == Elevation.Elevated && isWindows;
ProcessStartInfo startInfo = new()
{
FileName = filename,
Arguments = arguments,
CreateNoWindow = true,
};
if (useElevation)
{
startInfo.UseShellExecute = true;
startInfo.Verb = "runas";
}
else
{
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.StandardOutputEncoding = outputHandler.Encoding;
startInfo.StandardErrorEncoding = outputHandler.Encoding;
startInfo.UseShellExecute = false;
if (isWindows)
{
startInfo.LoadUserProfile = true;
}
}
using Process process = new() { StartInfo = startInfo };
process.Start();
if (useElevation)
{
await process.WaitForExitAsync().ConfigureAwait(false);
}
else
{
AsyncProcessStreamReader outputReader = new(process, outputHandler);
await Task.WhenAll(outputReader.Start(), process.WaitForExitAsync()).ConfigureAwait(false);
}
return process.ExitCode;
}
}