Skip to content

Commit

Permalink
Merge pull request #6 from perrylets/new-features
Browse files Browse the repository at this point in the history
Add new properties and crude access to the internal process
  • Loading branch information
perrylets authored Oct 1, 2023
2 parents 1dcc73f + a37c8b4 commit bf904ec
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 63 deletions.
28 changes: 18 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,31 @@ jobs:
runs-on: windows-latest

steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Run Tests
run: dotnet test --verbosity normal

- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build
- name: Run Tests
run: dotnet test --verbosity normal --no-build

test-on-linux:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build
- name: Run Tests
run: dotnet test --verbosity normal
run: dotnet test --verbosity normal --no-build
23 changes: 14 additions & 9 deletions CommandExec.Tests/EnvironmentTests.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
using System.Runtime.InteropServices;

namespace CommandExec.Tests;

public class EnvironmentTests
{
[Fact(DisplayName = "Run shell command")]
public void ShellCommandTest()
{
Command shell = Command.Shell("echo test");
Command shell = CommandUtils.Shell("echo", "test");
shell
.RedirectStdOut()
.RedirectStdErr()
.Run();

string STDOut = shell.STDOut.ReadToEnd().TrimEnd();
Assert.Equal("test", STDOut);

//! There can be errors with the shell itself, not the process.
// string STDErr = shell.STDErr.ReadToEnd().TrimEnd();
// Assert.Equal(string.Empty, STDErr);
Assert.Equal("test", shell.stdOut.ReadToEnd().TrimEnd());
Assert.Equal(0, shell.exitCode);
Assert.False(shell.hasError);
}

[Fact(DisplayName = "Check if command exists")]
public void CommandExistsTest()
{
Assert.True(CommandUtils.CommandExists("type"));
Assert.False(CommandUtils.CommandExists("fake-test-command-that-is-not-real"));
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Command cmd = CommandUtils.Shell("file", "/bin/sh").RedirectStdOut();
cmd.Run();
Assert.Equal("/bin/sh: symbolic link to", cmd.stdOut.ReadToEnd()[..25]);
}
Assert.True(CommandUtils.Exists("type"));
Assert.False(CommandUtils.Exists("fake-test-command-that-is-not-real"));
}
}
80 changes: 50 additions & 30 deletions CommandExec/Command.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
Expand All @@ -17,19 +18,39 @@ public class Command : IEnumerable
internal static readonly bool isUnix = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
#endregion

#region Standard Streams
#region Properties
/// <summary>
/// Gets the <see cref="StreamReader"/> for the Standard Output stream.
/// </summary>
public StreamReader STDOut => process.StandardOutput;
public StreamReader stdOut => process.StandardOutput;
/// <summary>
/// Gets the <see cref="StreamWriter"/> for the Standard Input stream.
/// </summary>
public StreamWriter STDIn => process.StandardInput;
public StreamWriter stdIn => process.StandardInput;
/// <summary>
/// Gets the <see cref="StreamReader"/> for the Standard Error stream.
/// </summary>
public StreamReader STDErr => process.StandardError;
public StreamReader stdErr => process.StandardError;
/// <summary>
/// Gets the exit code of the process.
/// </summary>
public int exitCode => process.ExitCode;
/// <summary>
/// Gets if the process had an error during execution.
/// </summary>
public bool hasError => exitCode != 0;
/// <summary>
/// Executed when the process exits.
/// </summary>
public event Action<Process>? onExit;
/// <summary>
/// Gets the id of the process.
/// </summary>
public int id => process.Id;
/// <summary>
/// Gets the start time of the process.
/// </summary>
public DateTime startTime => process.StartTime;
#endregion

#region Functions
Expand All @@ -38,15 +59,15 @@ public class Command : IEnumerable
/// </summary>
/// <param name="command">The file to run.</param>
/// <param name="cwd">The working directory of the process. If null, defaults to the current working directory.</param>
/// <param name="redirectSTDOut">Whether the textual output of an application is written to <see cref="STDOut"/>.</param>
/// <param name="redirectSTDIn">Wether the input for an application is read from <see cref="STDIn"/>.</param>
/// <param name="redirectSTDErr">Whether the error output of an application is written to <see cref="STDErr"/>.</param>
/// <param name="redirectSTDOut">Whether the textual output of an application is written to <see cref="stdOut"/>.</param>
/// <param name="redirectSTDIn">Wether the input for an application is read from <see cref="stdIn"/>.</param>
/// <param name="redirectSTDErr">Whether the error output of an application is written to <see cref="stdErr"/>.</param>
/// <param name="args">The process arguments.</param>
public Command(string command, string? cwd = null, bool redirectSTDOut = false, bool redirectSTDIn = false, bool redirectSTDErr = false, params string[] args)
{
process = new Process();
process.StartInfo.FileName = command;

process.Exited += OnExit;
process.StartInfo.WorkingDirectory = cwd ?? Directory.GetCurrentDirectory();
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
Expand Down Expand Up @@ -82,29 +103,21 @@ public Task RunAsync(params string[] args)
}

/// <summary>
/// Runs a command inside a shell. CMD on WIndows and Bash everywhere else.
/// Runs the process.
/// </summary>
/// <param name="command">The command to run in the shell.</param>
/// <param name="args">Additional arguments passed to the shell command.</param>
/// <returns>The command used to run the shell.</returns>
public static Command Shell(params string[] args)
/// <returns>
/// The process instance.
/// </returns>
public Process RawStart()
{
(string shellCommand, string shellArg) = isUnix ? ("bash", "-c") : ("powershell", string.Empty);
Command shell = new Command(shellCommand)
.AddArg(shellArg);

if (isUnix)
{
return shell.AddArg($"\"{string.Join(" ", args).Replace("\"", "\\\"")}");
}
return shell.AddArg(args);

process.Start();
return process;
}

/// <summary>
/// Adds a argument to the command.
/// </summary>
/// <param name="str">The argument to add.</param>
/// <param name="args">The argument to add.</param>
/// <returns>The command instance (for chaining).</returns>
/// <remarks>
/// <see cref="Add"/> is an alias for <see cref="AddArg"/>.
Expand All @@ -128,9 +141,9 @@ public Command CWD(string dir)
}

/// <summary>
/// Sets whether the textual output of an application is written to <see cref="STDOut"/>.
/// Sets whether the textual output of an application is written to <see cref="stdOut"/>.
/// </summary>
/// <param name="redirect">Whether the textual output of an application is written to <see cref="STDOut"/>.</param>
/// <param name="redirect">Whether the textual output of an application is written to <see cref="stdOut"/>.</param>
/// <returns>The command instance (for chaining).</returns>
public Command RedirectStdOut(bool redirect = true)
{
Expand All @@ -139,9 +152,9 @@ public Command RedirectStdOut(bool redirect = true)
}

/// <summary>
/// Sets whether the input for an application is read from <see cref="STDIn"/>.
/// Sets whether the input for an application is read from <see cref="stdIn"/>.
/// </summary>
/// <param name="redirect">Whether the input for an application is read from <see cref="STDIn"/>.</param>
/// <param name="redirect">Whether the input for an application is read from <see cref="stdIn"/>.</param>
/// <returns>The command instance (for chaining).</returns>
public Command RedirectStdIn(bool redirect = true)
{
Expand All @@ -150,9 +163,9 @@ public Command RedirectStdIn(bool redirect = true)
}

/// <summary>
/// Sets whether the error output of an application is written to <see cref="STDErr"/>.
/// Sets whether the error output of an application is written to <see cref="stdErr"/>.
/// </summary>
/// <param name="redirect">Whether the error output of an application is written to <see cref="STDErr"/>.</param>
/// <param name="redirect">Whether the error output of an application is written to <see cref="stdErr"/>.</param>
/// <returns>The command instance (for chaining).</returns>
public Command RedirectStdErr(bool redirect = true)
{
Expand All @@ -161,6 +174,13 @@ public Command RedirectStdErr(bool redirect = true)
}
#endregion

#region Private Functions
void OnExit(object? sender, EventArgs e)
{
onExit?.Invoke(process);
}
#endregion

#region Required Functions
/// <summary>
/// Adds a argument to the command.
Expand Down
2 changes: 1 addition & 1 deletion CommandExec/CommandExec.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<RootNamespace>CommandExec</RootNamespace>
<PublishRelease>true</PublishRelease>
<PackRelease>true</PackRelease>
<NoWarn>IDE1006;IDE0090</NoWarn>
<NoWarn>$(NoWarn);IDE1006;IDE0090</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
38 changes: 25 additions & 13 deletions CommandExec/CommandUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,38 @@ namespace CommandExec
{
public static class CommandUtils
{
public static bool CommandExists(string command)
/// <summary>
/// Runs a command inside a shell. CMD on WIndows and Bash everywhere else.
/// </summary>
/// <param name="command">The command to run in the shell.</param>
/// <param name="args">Additional arguments passed to the shell command.</param>
/// <returns>The command used to run the shell.</returns>
public static Command Shell(params string[] args)
{
(string shellCommand, string shellArg) = Command.isUnix ?
("/bin/sh", "-c") :
("powershell", "-Command");

Command shell = new Command(shellCommand).AddArg(shellArg);
return shell.AddArg($"\"{string.Join(" ", args).Replace("\"", "\\\"")}");
}

public static bool Exists(string command)
{
Command cmd;

if (Command.isUnix)
{
cmd = Command.Shell($"command -v {command} &> /dev/null").RedirectStdOut()
.RedirectStdOut()
.RedirectStdErr()
.RedirectStdIn();
}
else
{
cmd = Command.Shell($"Get-Command -Name {command} -ErrorAction SilentlyContinue > $null")
.RedirectStdOut()
.RedirectStdErr()
.RedirectStdIn();
cmd = Shell($"command", "-v", command).RedirectStdOut().RedirectStdErr()
.RedirectStdOut().RedirectStdErr();
cmd.Run();
return !cmd.hasError;
}

cmd = Shell("Get-Command", "-Name", command, "-ErrorAction", "Stop")
.RedirectStdOut().RedirectStdErr();
cmd.Run();
return cmd.process.ExitCode == 0;
return !cmd.hasError;
}
}
}

0 comments on commit bf904ec

Please sign in to comment.