{"id":13445678,"url":"https://github.com/twitchax/Sheller","last_synced_at":"2025-03-21T05:31:01.624Z","repository":{"id":89805297,"uuid":"166526918","full_name":"twitchax/Sheller","owner":"twitchax","description":"A .NET library that makes shelling out commands super easy and fluent.","archived":false,"fork":false,"pushed_at":"2020-03-17T18:04:12.000Z","size":129,"stargazers_count":107,"open_issues_count":0,"forks_count":7,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-02-20T19:19:21.143Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/twitchax.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":["twitchax"]}},"created_at":"2019-01-19T08:58:31.000Z","updated_at":"2025-01-23T14:15:27.000Z","dependencies_parsed_at":"2023-06-09T00:45:26.808Z","dependency_job_id":null,"html_url":"https://github.com/twitchax/Sheller","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twitchax%2FSheller","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twitchax%2FSheller/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twitchax%2FSheller/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twitchax%2FSheller/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twitchax","download_url":"https://codeload.github.com/twitchax/Sheller/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244745681,"owners_count":20503044,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T05:00:37.748Z","updated_at":"2025-03-21T05:31:01.315Z","avatar_url":"https://github.com/twitchax.png","language":"C#","funding_links":["https://github.com/sponsors/twitchax"],"categories":["Frameworks, Libraries and Tools","框架, 库和工具"],"sub_categories":["Misc","大杂烩"],"readme":"# Sheller\n\n[![Actions Status](https://github.com/twitchax/Sheller/workflows/build/badge.svg)](https://github.com/twitchax/AspNetCore.Proxy/actions)\n[![codecov](https://codecov.io/gh/twitchax/Sheller/branch/master/graph/badge.svg)](https://codecov.io/gh/twitchax/Sheller)\n[![GitHub Release](https://img.shields.io/github/release/twitchax/sheller.svg)](https://github.com/twitchax/Sheller/releases)\n[![NuGet Version](https://img.shields.io/nuget/v/Sheller.svg)](https://www.nuget.org/packages/Sheller/)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/Sheller.svg)](https://www.nuget.org/packages/Sheller/)\n\nA .NET library that makes shelling out commands super easy and fluent.  Think of it as a way to build human-readable shell scripts with the power of a full programming language.\n\n## Information\n\n### Install\n\n```bash\ndotnet add package Sheller\n```\n\n### Test\n\nDownload the source and run.\n\n```bash\ndotnet test --filter os~nix\n```\n\nOr.\n\n```bash\ndotnet test --filter os~win\n```\n\n\u003e NOTE: Windows tests require WSL.\n\n### Compatibility\n\nLatest .NET Standard 2.0.\n\n### Examples\n\nThis library is extendable, but you can run it a few ways depending on how you have extended it.  For more examples, check out the [tests](src/Test/BasicTests.cs).\n\nJust as one would expect from `IEnumerable`, and other fluent interface designs, the result of every call on `IShell` and `IExecutable` is a _new_ instance.  This means that you can reuse useful statements.\n\nIn addition, _future_ calls will not affect any previous instances, meaning you can safely chain them without affecting the calling instance.  A good example of this behavior is the fact that `myEnumerable.Where(...)` does not affect `myEnumerable`.\n\nIn general `With` methods can be called multiple times (multiple entries are allowed per execution context) and `Use` methods can only be called once (only one entry is allowed per execution context).  The context changes from `IShell` to `IExecutable` upon calling `IShell.UseExecutable`, so the methods available will be different once that method is called.\n\n#### Basic\n\nWith no extensions, you would run a command like this.\n\n```csharp\nvar echoResult = await Sheller.Builder\n    .UseShell(\"/bin/bash\")\n        .WithEnvironmentVariable(\"MY_VAR\", \"lol\")\n    .UseExecutable(\"echo\")\n        .WithArgument(\"$MY_VAR\")\n    .ExecuteAsync();\n\nvar exitCode = result.ExitCode; // 0\nvar standardOutput = result.StandardOutput; // \"lol\\n\"\nvar standardError = result.StandardError; // \"\"\nvar startTime = result.StartTime;\nvar endTime = result.EndTime;\nvar runTime = result.RunTime;\n```\n\nHowever, you can build your own custom `IShell` and `IExecutable` implementations that yield code that looks like this (Sheller ships with `Bash` and `Echo` by default).\n\n```csharp\nvar result = await Sheller.Builder\n    .UseShell\u003cBash\u003e()\n        .WithEnvironmentVariable(\"MY_VAR\", varValue)\n    .UseExecutable\u003cEcho\u003e()\n        .WithArgument(\"$MY_VAR\")\n    .ExecuteAsync();\n\nvar echoValue = result; // \"lol\"\n```\n\n#### Reusable Objects\n\nJust like one would expect from `IEnumerable`, and other fluent designs, the result of every call on `IShell` and `IExecutable` is a _new_ instance.  This means that you can reuse useful statements.\n\nIn addition, _future_ calls will not affect any previous instances, meaning you can safely chain them without affecting the calling instance.  A good example of this behavior is the fact that `myEnumerable.Where(...)` does not affect `myEnumerable`.\n\n```csharp\nvar shell = Builder.UseShell\u003cBash\u003e().WithEnvironmentVariable(\"MY_VAR\", \"cool\");\n\nvar anotherShell1 = shell.WithEnvironmentVariable(\"MY_VAR_2\", \"beans\");\nvar anotherShell2 = shell.WithEnvironmentVariable(\"MY_VAR_2\", \"stuff\");\n\nvar echoResult1 = anotherShell1.UseExecutable\u003cEcho\u003e().WithArgument(\"$MY_VAR$MY_VAR_2\").ExecuteAsync(); //result: \"coolbeans\".\nvar echoResult2 = anotherShell1.UseExecutable\u003cEcho\u003e().WithArgument(\"$MY_VAR$MY_VAR_2\").ExecuteAsync(); //result: \"coolstuff\".\n\n```\n\n#### Environment Variables\n\nYou can set environment variables on the shell.\n\n```csharp\nvar result = await Builder\n    .UseShell\u003cBash\u003e().WithEnvironmentVariable(\"MY_VAR\", expected)\n    .UseExecutable\u003cEcho\u003e().WithArgument(\"$MY_VAR\")\n    .ExecuteAsync();\n```\n\n#### Standard Output/Error\n\nYou can capture standard output and standard error.  These methods can be called multiple times.\n\n```csharp\nawait Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable\u003cEcho\u003e()\n        .WithArgument(\"saythings\")\n        .WithStandardOutputHandler(Console.WriteLine)\n        .WithStandardErrorHandler()\n    .ExecuteAsync();\n```\n\n#### Standard Input\n\nYou can pass standard input that gets captured during execution.  This method may be called multiple times.\n\n```csharp\nvar echoResult = await Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable(\"read var1; read var2; echo $var1$var2\")\n        .WithStandardInput(\"cool\")\n        .WithStandardInput(\"library\")\n    .ExecuteAsync();\n\n// echoResult is \"coollibrary\".\n```\n\n#### Input Request Handler\n\nYou can provide a handler that will be called when execution is blocked and waiting for input.  This can only be set once per execution context.\n\n```csharp\nvar echoResult = await Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable($\"read var1; echo $var1\")\n    .UseInputRequestHandler((stdout, stderr) =\u003e\n    {\n        // Process stdout or stderr, if needed.\n        // Process is blocked, waiting for this input.\n        return Task.FromResult(\"awesome\");\n    })\n    .ExecuteAsync();\n\n// echoResult is \"awesome\".\n```\n\n#### No Throw\n\nYou can instruct the execution to _not_ throw on a non-zero exit code.  This method may only be called once.\n\n```csharp\nawait Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable(\"foo\")\n    .UseNoThrow()\n    .ExecuteAsync();\n```\n\n#### Result Selector\n\nYou can pass a map function to `ExecuteAsync` to \"select\" the result.\n\n```csharp\nvar echoErrorCode = await Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable\u003cEcho\u003e().WithArgument(\"dummy\")\n    .ExecuteAsync(cr =\u003e \n    {\n        return cr.ExitCode;\n    });\n\n// echoErrorCode is 0.\n```\n\n#### Execution Timeout\n\nYou can set the execution timeout.  This method can only be called once.\n\n```csharp\nawait Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable\u003cSleep\u003e()\n        .WithArgument(\"10\")\n        .UseTimeout(TimeSpan.FromSeconds(5))\n    .ExecuteAsync();\n```\n\n#### Post Execution Wait\n\nYou can provide waiters that process the result and wait upon a task before completion of the outer task.  These waiters may be applied multiple times.  You can also set a wait timeout (only one) with `UseWaitTimeout`.\n\n```csharp\nvar echoValue = await Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable\u003cEcho\u003e()\n        .WithArgument(\"dummy\")\n        .WithWait(async cr =\u003e \n        {\n            return SomethingThatIsATask();\n        })\n        .UseWaitTimeout(TimeSpan.FromSeconds(30))\n    .ExecuteAsync();\n```\n\n#### Reactive\n\nYou can subscribe to an internal `IObservable`.  You can call the subscriber multiple times.\n\n```csharp\nvar events = new List\u003cstring\u003e();\n\nvar command1 = Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable(\"echo\")\n    .WithArgument(expected)\n    .WithSubscribe(o =\u003e\n    {\n        o.Where(ev =\u003e ev.Type == CommandEventType.StandardOutput).Select(ev =\u003e ev.Data).Do(data =\u003e\n        {\n            events.Add(data);\n        }).Subscribe();\n    })\n    .ExecuteAsync();\n```\n\n#### Cancellation\n\nYou can set cancellation tokens.  This method may be called multiple times.\n\n```csharp\nusing (var ctSource = new CancellationTokenSource())\n{\n    ctSource.CancelAfter(TimeSpan.FromSeconds(5));\n\n    Builder\n        .UseShell\u003cBash\u003e()\n        .UseExecutable\u003cSleep\u003e()\n        .WithArgument(\"10\")\n        .WithCancellationToken(ctSource.Token)\n        .ExecuteAsync();\n}\n```\n\n#### Character Encoding\n\nYou can set the output character encoding.  This method may only be called once.\n\n```csharp\nvar echoValue = await Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable\u003cEcho\u003e()\n        .WithArgument(\"😋\")\n        .UseStandardOutputEncoding(Encoding.ASCII)\n    .ExecuteAsync();\n\n// echoValue is \"????\".\n```\n\n#### `StartInfo` Transform\n\nYou can arbitrarily change any options on the `StartInfo` of the process.  This method may only be called once.\n\n```csharp\nvar echoValue = await Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable\u003cEcho\u003e()\n        .WithArgument(expected)\n        .UseStartInfoTransform(si =\u003e \n        {\n            si.WorkingDirectory = \"/\";\n        })\n    .ExecuteAsync();\n```\n\n#### Roll Your Own\n\nYou can also roll your own `IShell` and `IExecutable` plugins.  For example, it would be nice to implement a `kubectl` wrapper.\n\n```csharp\npublic interface IKubectl : IExecutable\u003cIKubectl\u003e\n{\n    IKubectl WithKubeConfig(string configPath);\n    IKubectl WithApply(string yamlPath);\n}\n\npublic class Kubectl : Executable\u003cIKubectl\u003e, IKubectl\n{\n    // Allows the `Executable` base class to \"create and clone\" a new instance for chanining.\n    protected override Executable\u003cIKubectl\u003e Create() =\u003e new Kubectl();\n    // Sets the underlying executable of this executable type.\n    public Kubectl() : base(\"kubectl\") {}\n    \n    public IKubectl WithKubeConfig(string configPath) =\u003e this.WithArgument($\"--kubeconfig={configPath}\");\n    public IKubectl WithApply(string yamlPath) =\u003e this.WithArgument(\"apply\", \"-f\", yamlPath);\n}\n\n...\n\nvar result = await Builder\n    .UseShell\u003cBash\u003e()\n    .UseExecutable\u003cKubectl\u003e()\n        .WithKubeConfig(\"kube_config.yaml\")\n        .WithApply(\"my_app.yaml\")\n    .ExecuteAsync();\n```\n\n## License\n\n```\nThe MIT License (MIT)\n\nCopyright (c) 2019 Aaron Roney\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwitchax%2FSheller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwitchax%2FSheller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwitchax%2FSheller/lists"}