Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7
A .NET library that makes shelling out commands super easy and fluent.
License
twitchax/Sheller
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
A .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.
dotnet add package Sheller
Download the source and run.
dotnettest --filter os~nix
Or.
dotnettest --filter os~win
NOTE: Windows tests require WSL.
Latest .NET Standard 2.0.
This library is extendable, but you can run it a few ways depending on how you have extended it. For more examples, check out thetests.
Just as one would expect fromIEnumerable
, and other fluent interface designs, the result of every call onIShell
andIExecutable
is anew instance. This means that you can reuse useful statements.
In 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 thatmyEnumerable.Where(...)
does not affectmyEnumerable
.
In generalWith
methods can be called multiple times (multiple entries are allowed per execution context) andUse
methods can only be called once (only one entry is allowed per execution context). The context changes fromIShell
toIExecutable
upon callingIShell.UseExecutable
, so the methods available will be different once that method is called.
With no extensions, you would run a command like this.
varechoResult=awaitSheller.Builder.UseShell("/bin/bash").WithEnvironmentVariable("MY_VAR","lol").UseExecutable("echo").WithArgument("$MY_VAR").ExecuteAsync();varexitCode=result.ExitCode;// 0varstandardOutput=result.StandardOutput;// "lol\n"varstandardError=result.StandardError;// ""varstartTime=result.StartTime;varendTime=result.EndTime;varrunTime=result.RunTime;
However, you can build your own customIShell
andIExecutable
implementations that yield code that looks like this (Sheller ships withBash
andEcho
by default).
varresult=awaitSheller.Builder.UseShell<Bash>().WithEnvironmentVariable("MY_VAR",varValue).UseExecutable<Echo>().WithArgument("$MY_VAR").ExecuteAsync();varechoValue=result;// "lol"
Just like one would expect fromIEnumerable
, and other fluent designs, the result of every call onIShell
andIExecutable
is anew instance. This means that you can reuse useful statements.
In 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 thatmyEnumerable.Where(...)
does not affectmyEnumerable
.
varshell=Builder.UseShell<Bash>().WithEnvironmentVariable("MY_VAR","cool");varanotherShell1=shell.WithEnvironmentVariable("MY_VAR_2","beans");varanotherShell2=shell.WithEnvironmentVariable("MY_VAR_2","stuff");varechoResult1=anotherShell1.UseExecutable<Echo>().WithArgument("$MY_VAR$MY_VAR_2").ExecuteAsync();//result: "coolbeans".varechoResult2=anotherShell1.UseExecutable<Echo>().WithArgument("$MY_VAR$MY_VAR_2").ExecuteAsync();//result: "coolstuff".
You can set environment variables on the shell.
varresult=awaitBuilder.UseShell<Bash>().WithEnvironmentVariable("MY_VAR",expected).UseExecutable<Echo>().WithArgument("$MY_VAR").ExecuteAsync();
You can capture standard output and standard error. These methods can be called multiple times.
awaitBuilder.UseShell<Bash>().UseExecutable<Echo>().WithArgument("saythings").WithStandardOutputHandler(Console.WriteLine).WithStandardErrorHandler().ExecuteAsync();
You can pass standard input that gets captured during execution. This method may be called multiple times.
varechoResult=awaitBuilder.UseShell<Bash>().UseExecutable("read var1; read var2; echo $var1$var2").WithStandardInput("cool").WithStandardInput("library").ExecuteAsync();// echoResult is "coollibrary".
You 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.
varechoResult=awaitBuilder.UseShell<Bash>().UseExecutable($"read var1; echo $var1").UseInputRequestHandler((stdout,stderr)=>{// Process stdout or stderr, if needed.// Process is blocked, waiting for this input.returnTask.FromResult("awesome");}).ExecuteAsync();// echoResult is "awesome".
You can instruct the execution tonot throw on a non-zero exit code. This method may only be called once.
awaitBuilder.UseShell<Bash>().UseExecutable("foo").UseNoThrow().ExecuteAsync();
You can pass a map function toExecuteAsync
to "select" the result.
varechoErrorCode=awaitBuilder.UseShell<Bash>().UseExecutable<Echo>().WithArgument("dummy").ExecuteAsync(cr=>{returncr.ExitCode;});// echoErrorCode is 0.
You can set the execution timeout. This method can only be called once.
awaitBuilder.UseShell<Bash>().UseExecutable<Sleep>().WithArgument("10").UseTimeout(TimeSpan.FromSeconds(5)).ExecuteAsync();
You 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) withUseWaitTimeout
.
varechoValue=awaitBuilder.UseShell<Bash>().UseExecutable<Echo>().WithArgument("dummy").WithWait(async cr=>{returnSomethingThatIsATask();}).UseWaitTimeout(TimeSpan.FromSeconds(30)).ExecuteAsync();
You can subscribe to an internalIObservable
. You can call the subscriber multiple times.
varevents=newList<string>();varcommand1=Builder.UseShell<Bash>().UseExecutable("echo").WithArgument(expected).WithSubscribe(o=>{o.Where(ev=>ev.Type==CommandEventType.StandardOutput).Select(ev=>ev.Data).Do(data=>{events.Add(data);}).Subscribe();}).ExecuteAsync();
You can set cancellation tokens. This method may be called multiple times.
using(varctSource=newCancellationTokenSource()){ctSource.CancelAfter(TimeSpan.FromSeconds(5));Builder.UseShell<Bash>().UseExecutable<Sleep>().WithArgument("10").WithCancellationToken(ctSource.Token).ExecuteAsync();}
You can set the output character encoding. This method may only be called once.
varechoValue=awaitBuilder.UseShell<Bash>().UseExecutable<Echo>().WithArgument("😋").UseStandardOutputEncoding(Encoding.ASCII).ExecuteAsync();// echoValue is "????".
You can arbitrarily change any options on theStartInfo
of the process. This method may only be called once.
varechoValue=awaitBuilder.UseShell<Bash>().UseExecutable<Echo>().WithArgument(expected).UseStartInfoTransform(si=>{si.WorkingDirectory="/";}).ExecuteAsync();
You can also roll your ownIShell
andIExecutable
plugins. For example, it would be nice to implement akubectl
wrapper.
publicinterfaceIKubectl:IExecutable<IKubectl>{IKubectlWithKubeConfig(stringconfigPath);IKubectlWithApply(stringyamlPath);}publicclassKubectl:Executable<IKubectl>,IKubectl{// Allows the `Executable` base class to "create and clone" a new instance for chanining.protectedoverrideExecutable<IKubectl>Create()=>newKubectl();// Sets the underlying executable of this executable type.publicKubectl():base("kubectl"){}publicIKubectlWithKubeConfig(stringconfigPath)=>this.WithArgument($"--kubeconfig={configPath}");publicIKubectlWithApply(stringyamlPath)=>this.WithArgument("apply","-f",yamlPath);}...var result=awaitBuilder.UseShell<Bash>().UseExecutable<Kubectl>().WithKubeConfig("kube_config.yaml").WithApply("my_app.yaml").ExecuteAsync();
The MIT License (MIT)Copyright (c) 2019 Aaron RoneyPermission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE.
About
A .NET library that makes shelling out commands super easy and fluent.
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors2
Uh oh!
There was an error while loading.Please reload this page.