├── .editorconfig
├── .gitattributes
├── .github
└── workflows
│ ├── build.yaml
│ └── release.yaml
├── .gitignore
├── Chell.sln
├── Directory.Build.props
├── LICENSE
├── README.md
├── samples
├── GettingStarted.Basic.Unix
│ ├── GettingStarted.Basic.Unix.csproj
│ └── Program.cs
└── GettingStarted.Basic.Windows
│ ├── GettingStarted.Basic.Windows.csproj
│ └── Program.cs
├── src
├── .editorconfig
├── Chell.Run
│ ├── Chell.Run.csproj
│ └── Program.cs
└── Chell
│ ├── Chell.csproj
│ ├── ChellEnvironment.cs
│ ├── CommandLineString.cs
│ ├── Exports.cs
│ ├── Extensions
│ ├── ChellExtensions.cs
│ ├── ProcessOutputExtensions.cs
│ ├── ProcessTaskExtensions.Generated.cs
│ ├── ProcessTaskExtensions.cs
│ ├── ProcessTaskExtensions.tt
│ └── StringExtensions.cs
│ ├── IO
│ ├── ChellWrappedStream.cs
│ ├── ChellWritableStream.Generated.cs
│ ├── ChellWritableStream.tt
│ ├── IConsoleProvider.cs
│ ├── LINQPadConsoleProvider.cs
│ └── SystemConsoleProvider.cs
│ ├── Internal
│ ├── CommandLineHelper.cs
│ ├── EnvironmentVariables.cs
│ ├── LINQPadHelper.cs
│ ├── ObjectDumper.cs
│ ├── OutputSink.cs
│ ├── StandardInput.cs
│ ├── StreamPipe.cs
│ └── Which.cs
│ ├── ProcessOutput.cs
│ ├── ProcessTask.cs
│ ├── ProcessTaskException.cs
│ ├── ProcessTaskOptions.cs
│ ├── Run.cs
│ └── Shell
│ ├── BashShellExecutor.cs
│ ├── CmdShellExecutor.cs
│ ├── IShellExecutor.cs
│ ├── NoUseShellExecutor.cs
│ └── ShellExecutorProvider.cs
└── tests
└── Chell.Tests
├── Chell.Tests.csproj
├── ChellEnvironmentTest.cs
├── CommandLineStringTest.cs
├── ProcessTaskTest.cs
├── Shell
├── BashShellExecutorTest.cs
└── CmdShellExecutorTest.cs
└── TemporaryAppBuilder.cs
/.editorconfig:
--------------------------------------------------------------------------------
1 | # To learn more about .editorconfig see https://aka.ms/editorconfigdocs
2 | ###############################
3 | # Core EditorConfig Options #
4 | ###############################
5 | root = true
6 |
7 | # All files
8 | [*]
9 | indent_style = space
10 |
11 | # Code files
12 | [*.{cs,csx,vb,vbx}]
13 | indent_size = 4
14 | insert_final_newline = true
15 | charset = utf-8
16 |
17 | [*.md]
18 | charset = utf-8
19 |
20 | ###############################
21 | # .NET Coding Conventions #
22 | ###############################
23 | [*.{cs,vb}]
24 | # Organize usings
25 | dotnet_sort_system_directives_first = true
26 | # this. preferences
27 | dotnet_style_qualification_for_field = false:silent
28 | dotnet_style_qualification_for_property = false:silent
29 | dotnet_style_qualification_for_method = false:silent
30 | dotnet_style_qualification_for_event = false:silent
31 | # Language keywords vs BCL types preferences
32 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent
33 | dotnet_style_predefined_type_for_member_access = true:silent
34 | # Parentheses preferences
35 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
36 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
37 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
38 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
39 | # Modifier preferences
40 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
41 | dotnet_style_readonly_field = true:suggestion
42 | # Expression-level preferences
43 | dotnet_style_object_initializer = true:suggestion
44 | dotnet_style_collection_initializer = true:suggestion
45 | dotnet_style_explicit_tuple_names = true:suggestion
46 | dotnet_style_null_propagation = true:suggestion
47 | dotnet_style_coalesce_expression = true:suggestion
48 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
49 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
50 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
51 | dotnet_style_prefer_auto_properties = true:silent
52 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
53 | dotnet_style_prefer_conditional_expression_over_return = true:silent
54 | ###############################
55 | # Naming Conventions #
56 | ###############################
57 | # Style Definitions
58 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case
59 | # Use PascalCase for constant fields
60 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
61 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
62 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
63 | dotnet_naming_symbols.constant_fields.applicable_kinds = field
64 | dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
65 | dotnet_naming_symbols.constant_fields.required_modifiers = const
66 | ###############################
67 | # C# Coding Conventions #
68 | ###############################
69 | [*.cs]
70 | # var preferences
71 | csharp_style_var_for_built_in_types = true:silent
72 | csharp_style_var_when_type_is_apparent = true:silent
73 | csharp_style_var_elsewhere = true:silent
74 | # Expression-bodied members
75 | csharp_style_expression_bodied_methods = false:silent
76 | csharp_style_expression_bodied_constructors = false:silent
77 | csharp_style_expression_bodied_operators = false:silent
78 | csharp_style_expression_bodied_properties = true:silent
79 | csharp_style_expression_bodied_indexers = true:silent
80 | csharp_style_expression_bodied_accessors = true:silent
81 | # Pattern matching preferences
82 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
83 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
84 | # Null-checking preferences
85 | csharp_style_throw_expression = true:suggestion
86 | csharp_style_conditional_delegate_call = true:suggestion
87 | # Modifier preferences
88 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
89 | # Expression-level preferences
90 | csharp_prefer_braces = true:silent
91 | csharp_style_deconstructed_variable_declaration = true:suggestion
92 | csharp_prefer_simple_default_expression = true:suggestion
93 | csharp_style_pattern_local_over_anonymous_function = true:suggestion
94 | csharp_style_inlined_variable_declaration = true:suggestion
95 | ###############################
96 | # C# Formatting Rules #
97 | ###############################
98 | # New line preferences
99 | csharp_new_line_before_open_brace = all
100 | csharp_new_line_before_else = true
101 | csharp_new_line_before_catch = true
102 | csharp_new_line_before_finally = true
103 | csharp_new_line_before_members_in_object_initializers = true
104 | csharp_new_line_before_members_in_anonymous_types = true
105 | csharp_new_line_between_query_expression_clauses = true
106 | # Indentation preferences
107 | csharp_indent_case_contents = true
108 | csharp_indent_switch_labels = true
109 | csharp_indent_labels = flush_left
110 | # Space preferences
111 | csharp_space_after_cast = false
112 | csharp_space_after_keywords_in_control_flow_statements = true
113 | csharp_space_between_method_call_parameter_list_parentheses = false
114 | csharp_space_between_method_declaration_parameter_list_parentheses = false
115 | csharp_space_between_parentheses = false
116 | csharp_space_before_colon_in_inheritance_clause = true
117 | csharp_space_after_colon_in_inheritance_clause = true
118 | csharp_space_around_binary_operators = before_and_after
119 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
120 | csharp_space_between_method_call_name_and_opening_parenthesis = false
121 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
122 | # Wrapping preferences
123 | csharp_preserve_single_line_statements = true
124 | csharp_preserve_single_line_blocks = true
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | *.sh text eol=lf
7 |
8 | ###############################################################################
9 | # Set default behavior for command prompt diff.
10 | #
11 | # This is need for earlier builds of msysgit that does not have it on by
12 | # default for csharp files.
13 | # Note: This is only used by command line
14 | ###############################################################################
15 | #*.cs diff=csharp
16 |
17 | ###############################################################################
18 | # Set the merge driver for project and solution files
19 | #
20 | # Merging from the command prompt will add diff markers to the files if there
21 | # are conflicts (Merging from VS is not affected by the settings below, in VS
22 | # the diff markers are never inserted). Diff markers may cause the following
23 | # file extensions to fail to load in VS. An alternative would be to treat
24 | # these files as binary and thus will always conflict and require user
25 | # intervention with every merge. To do so, just uncomment the entries below
26 | ###############################################################################
27 | #*.sln merge=binary
28 | #*.csproj merge=binary
29 | #*.vbproj merge=binary
30 | #*.vcxproj merge=binary
31 | #*.vcproj merge=binary
32 | #*.dbproj merge=binary
33 | #*.fsproj merge=binary
34 | #*.lsproj merge=binary
35 | #*.wixproj merge=binary
36 | #*.modelproj merge=binary
37 | #*.sqlproj merge=binary
38 | #*.wwaproj merge=binary
39 |
40 | ###############################################################################
41 | # behavior for image files
42 | #
43 | # image files are treated as binary by default.
44 | ###############################################################################
45 | #*.jpg binary
46 | #*.png binary
47 | #*.gif binary
48 |
49 | ###############################################################################
50 | # diff behavior for common document formats
51 | #
52 | # Convert binary document formats to text before diffing them. This feature
53 | # is only available from the command line. Turn it on by uncommenting the
54 | # entries below.
55 | ###############################################################################
56 | #*.doc diff=astextplain
57 | #*.DOC diff=astextplain
58 | #*.docx diff=astextplain
59 | #*.DOCX diff=astextplain
60 | #*.dot diff=astextplain
61 | #*.DOT diff=astextplain
62 | #*.pdf diff=astextplain
63 | #*.PDF diff=astextplain
64 | #*.rtf diff=astextplain
65 | #*.RTF diff=astextplain
66 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: Build-Development
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - master
8 | pull_request:
9 | types:
10 | - opened
11 | - synchronize
12 |
13 | jobs:
14 | Build:
15 | runs-on: ${{ matrix.os }}
16 | strategy:
17 | matrix:
18 | os: [windows-latest, ubuntu-latest, macos-latest]
19 | env:
20 | DOTNET_NOLOGO: true
21 | steps:
22 | - uses: actions/checkout@v1
23 | - uses: actions/setup-dotnet@v1
24 | with:
25 | dotnet-version: '5.0.x'
26 |
27 | # Build
28 | - run: dotnet restore
29 | - run: dotnet build -c Release
30 |
31 | # Run Unit tests
32 | - run: dotnet test -c Release --no-build --logger trx --results-directory $GITHUB_WORKSPACE/artifacts
33 |
34 | # Packaging
35 | - name: dotnet pack
36 | run: dotnet pack -c Release --no-build -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output $GITHUB_WORKSPACE/artifacts
37 | shell: bash
38 |
39 | # Upload & Publish
40 | - uses: actions/upload-artifact@master
41 | with:
42 | name: Packages
43 | path: artifacts
--------------------------------------------------------------------------------
/.github/workflows/release.yaml:
--------------------------------------------------------------------------------
1 | name: Build-Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - v*
7 |
8 | jobs:
9 | Release:
10 | if: "contains(github.ref, 'refs/tags')"
11 | runs-on: ubuntu-latest
12 | env:
13 | DOTNET_NOLOGO: true
14 | steps:
15 | - uses: actions/checkout@v1
16 | - uses: actions/setup-dotnet@v1
17 | with:
18 | dotnet-version: '5.0.x'
19 |
20 | - name: "Set VersionSuffix for Preview"
21 | if: "contains(github.ref, 'refs/tags') && contains(github.ref, 'preview')"
22 | run: |
23 | echo "VERSION_SUFFIX=preview.`date '+%Y%m%d-%H%M%S'`+${GITHUB_SHA:0:6}" >> $GITHUB_ENV
24 | - name: "Get git tag"
25 | if: "contains(github.ref, 'refs/tags')"
26 | run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
27 |
28 | # Build
29 | - run: dotnet restore
30 | - run: dotnet build -c Release
31 |
32 | # Packaging
33 | - name: dotnet pack
34 | run: dotnet pack -c Release --no-build --version-suffix "$VERSION_SUFFIX" -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output $GITHUB_WORKSPACE/artifacts
35 |
36 | # Upload & Publish
37 | - uses: actions/upload-artifact@master
38 | with:
39 | name: Packages
40 | path: artifacts
41 |
42 | - name: "Push to NuGet.org"
43 | run: |
44 | dotnet nuget push "$GITHUB_WORKSPACE/artifacts/*.nupkg" --skip-duplicate -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | .DS_Store
7 |
8 | # User-specific files
9 | *.rsuser
10 | *.suo
11 | *.user
12 | *.userosscache
13 | *.sln.docstates
14 |
15 | # User-specific files (MonoDevelop/Xamarin Studio)
16 | *.userprefs
17 |
18 | # Build results
19 | [Dd]ebug/
20 | [Dd]ebugPublic/
21 | [Rr]elease/
22 | [Rr]eleases/
23 | x64/
24 | x86/
25 | [Aa][Rr][Mm]/
26 | [Aa][Rr][Mm]64/
27 | bld/
28 | [Bb]in/
29 | [Oo]bj/
30 | [Ll]og/
31 |
32 | # Visual Studio 2015/2017 cache/options directory
33 | .vs/
34 | # Uncomment if you have tasks that create the project's static files in wwwroot
35 | #wwwroot/
36 |
37 | # Visual Studio 2017 auto generated files
38 | Generated\ Files/
39 |
40 | # MSTest test Results
41 | [Tt]est[Rr]esult*/
42 | [Bb]uild[Ll]og.*
43 |
44 | # NUNIT
45 | *.VisualState.xml
46 | TestResult.xml
47 |
48 | # Build Results of an ATL Project
49 | [Dd]ebugPS/
50 | [Rr]eleasePS/
51 | dlldata.c
52 |
53 | # Benchmark Results
54 | BenchmarkDotNet.Artifacts/
55 |
56 | # .NET Core
57 | project.lock.json
58 | project.fragment.lock.json
59 | artifacts/
60 |
61 | # StyleCop
62 | StyleCopReport.xml
63 |
64 | # Files built by Visual Studio
65 | *_i.c
66 | *_p.c
67 | *_h.h
68 | *.ilk
69 | *.meta
70 | *.obj
71 | *.iobj
72 | *.pch
73 | *.pdb
74 | *.ipdb
75 | *.pgc
76 | *.pgd
77 | *.rsp
78 | *.sbr
79 | *.tlb
80 | *.tli
81 | *.tlh
82 | *.tmp
83 | *.tmp_proj
84 | *_wpftmp.csproj
85 | *.log
86 | *.vspscc
87 | *.vssscc
88 | .builds
89 | *.pidb
90 | *.svclog
91 | *.scc
92 |
93 | # Chutzpah Test files
94 | _Chutzpah*
95 |
96 | # Visual C++ cache files
97 | ipch/
98 | *.aps
99 | *.ncb
100 | *.opendb
101 | *.opensdf
102 | *.sdf
103 | *.cachefile
104 | *.VC.db
105 | *.VC.VC.opendb
106 |
107 | # Visual Studio profiler
108 | *.psess
109 | *.vsp
110 | *.vspx
111 | *.sap
112 |
113 | # Visual Studio Trace Files
114 | *.e2e
115 |
116 | # TFS 2012 Local Workspace
117 | $tf/
118 |
119 | # Guidance Automation Toolkit
120 | *.gpState
121 |
122 | # ReSharper is a .NET coding add-in
123 | _ReSharper*/
124 | *.[Rr]e[Ss]harper
125 | *.DotSettings.user
126 |
127 | # JustCode is a .NET coding add-in
128 | .JustCode
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | *.snupkg
188 | # The packages folder can be ignored because of Package Restore
189 | **/[Pp]ackages/*
190 | # except build/, which is used as an MSBuild target.
191 | !**/[Pp]ackages/build/
192 | # Uncomment if necessary however generally it will be regenerated when needed
193 | #!**/[Pp]ackages/repositories.config
194 | # NuGet v3's project.json files produces more ignorable files
195 | *.nuget.props
196 | *.nuget.targets
197 |
198 | # Microsoft Azure Build Output
199 | csx/
200 | *.build.csdef
201 |
202 | # Microsoft Azure Emulator
203 | ecf/
204 | rcf/
205 |
206 | # Windows Store app package directories and files
207 | AppPackages/
208 | BundleArtifacts/
209 | Package.StoreAssociation.xml
210 | _pkginfo.txt
211 | *.appx
212 |
213 | # Visual Studio cache files
214 | # files ending in .cache can be ignored
215 | *.[Cc]ache
216 | # but keep track of directories ending in .cache
217 | !?*.[Cc]ache/
218 |
219 | # Others
220 | ClientBin/
221 | ~$*
222 | *~
223 | *.dbmdl
224 | *.dbproj.schemaview
225 | *.jfm
226 | *.pfx
227 | *.publishsettings
228 | orleans.codegen.cs
229 |
230 | # Including strong name files can present a security risk
231 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
232 | #*.snk
233 |
234 | # Since there are multiple workflows, uncomment next line to ignore bower_components
235 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
236 | #bower_components/
237 |
238 | # RIA/Silverlight projects
239 | Generated_Code/
240 |
241 | # Backup & report files from converting an old project file
242 | # to a newer Visual Studio version. Backup files are not needed,
243 | # because we have git ;-)
244 | _UpgradeReport_Files/
245 | Backup*/
246 | UpgradeLog*.XML
247 | UpgradeLog*.htm
248 | ServiceFabricBackup/
249 | *.rptproj.bak
250 |
251 | # SQL Server files
252 | *.mdf
253 | *.ldf
254 | *.ndf
255 |
256 | # Business Intelligence projects
257 | *.rdl.data
258 | *.bim.layout
259 | *.bim_*.settings
260 | *.rptproj.rsuser
261 | *- Backup*.rdl
262 |
263 | # Microsoft Fakes
264 | FakesAssemblies/
265 |
266 | # GhostDoc plugin setting file
267 | *.GhostDoc.xml
268 |
269 | # Node.js Tools for Visual Studio
270 | .ntvs_analysis.dat
271 | node_modules/
272 |
273 | # Visual Studio 6 build log
274 | *.plg
275 |
276 | # Visual Studio 6 workspace options file
277 | *.opt
278 |
279 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
280 | *.vbw
281 |
282 | # Visual Studio LightSwitch build output
283 | **/*.HTMLClient/GeneratedArtifacts
284 | **/*.DesktopClient/GeneratedArtifacts
285 | **/*.DesktopClient/ModelManifest.xml
286 | **/*.Server/GeneratedArtifacts
287 | **/*.Server/ModelManifest.xml
288 | _Pvt_Extensions
289 |
290 | # Paket dependency manager
291 | .paket/paket.exe
292 | paket-files/
293 |
294 | # FAKE - F# Make
295 | .fake/
296 |
297 | # JetBrains Rider
298 | .idea/
299 | *.sln.iml
300 |
301 | # CodeRush personal settings
302 | .cr/personal
303 |
304 | # Python Tools for Visual Studio (PTVS)
305 | __pycache__/
306 | *.pyc
307 |
308 | # Cake - Uncomment if you are using it
309 | # tools/**
310 | # !tools/packages.config
311 |
312 | # Tabs Studio
313 | *.tss
314 |
315 | # Telerik's JustMock configuration file
316 | *.jmconfig
317 |
318 | # BizTalk build output
319 | *.btp.cs
320 | *.btm.cs
321 | *.odx.cs
322 | *.xsd.cs
323 |
324 | # OpenCover UI analysis results
325 | OpenCover/
326 |
327 | # Azure Stream Analytics local run output
328 | ASALocalRun/
329 |
330 | # MSBuild Binary and Structured Log
331 | *.binlog
332 |
333 | # NVidia Nsight GPU debugger configuration file
334 | *.nvuser
335 |
336 | # MFractors (Xamarin productivity tool) working folder
337 | .mfractor/
338 |
339 | # Local History for Visual Studio
340 | .localhistory/
341 |
342 | # BeatPulse healthcheck temp database
343 | healthchecksdb
344 |
345 | # .NET Launch Profiles
346 | launchSettings.json
--------------------------------------------------------------------------------
/Chell.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30114.105
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chell", "src\Chell\Chell.csproj", "{6BE659EC-A00D-4148-B19D-B5478DE001FA}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chell.Run", "src\Chell.Run\Chell.Run.csproj", "{893D8C70-3C13-47D6-987F-C099C6161D7E}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chell.Tests", "tests\Chell.Tests\Chell.Tests.csproj", "{1FBAA8ED-438E-498C-AB1F-29429550DC21}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EBB92712-D878-48FD-8E31-E577AABDC0FB}"
13 | ProjectSection(SolutionItems) = preProject
14 | .gitignore = .gitignore
15 | .github\workflows\build.yaml = .github\workflows\build.yaml
16 | Directory.Build.props = Directory.Build.props
17 | README.md = README.md
18 | .github\workflows\release.yaml = .github\workflows\release.yaml
19 | EndProjectSection
20 | EndProject
21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{AF454F92-8B84-446B-B0E2-9BA8887B09CC}"
22 | EndProject
23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GettingStarted.Basic.Windows", "samples\GettingStarted.Basic.Windows\GettingStarted.Basic.Windows.csproj", "{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}"
24 | EndProject
25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GettingStarted.Basic.Unix", "samples\GettingStarted.Basic.Unix\GettingStarted.Basic.Unix.csproj", "{69DC1056-843E-4980-908A-5DB4ADA95460}"
26 | EndProject
27 | Global
28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
29 | Debug|Any CPU = Debug|Any CPU
30 | Debug|x64 = Debug|x64
31 | Debug|x86 = Debug|x86
32 | Release|Any CPU = Release|Any CPU
33 | Release|x64 = Release|x64
34 | Release|x86 = Release|x86
35 | EndGlobalSection
36 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
37 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|x64.ActiveCfg = Debug|Any CPU
40 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|x64.Build.0 = Debug|Any CPU
41 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|x86.ActiveCfg = Debug|Any CPU
42 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|x86.Build.0 = Debug|Any CPU
43 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|Any CPU.Build.0 = Release|Any CPU
45 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|x64.ActiveCfg = Release|Any CPU
46 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|x64.Build.0 = Release|Any CPU
47 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|x86.ActiveCfg = Release|Any CPU
48 | {6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|x86.Build.0 = Release|Any CPU
49 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|x64.ActiveCfg = Debug|Any CPU
52 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|x64.Build.0 = Debug|Any CPU
53 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|x86.ActiveCfg = Debug|Any CPU
54 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|x86.Build.0 = Debug|Any CPU
55 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|Any CPU.Build.0 = Release|Any CPU
57 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|x64.ActiveCfg = Release|Any CPU
58 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|x64.Build.0 = Release|Any CPU
59 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|x86.ActiveCfg = Release|Any CPU
60 | {893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|x86.Build.0 = Release|Any CPU
61 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|Any CPU.Build.0 = Debug|Any CPU
63 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|x64.ActiveCfg = Debug|Any CPU
64 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|x64.Build.0 = Debug|Any CPU
65 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|x86.ActiveCfg = Debug|Any CPU
66 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|x86.Build.0 = Debug|Any CPU
67 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|Any CPU.ActiveCfg = Release|Any CPU
68 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|Any CPU.Build.0 = Release|Any CPU
69 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|x64.ActiveCfg = Release|Any CPU
70 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|x64.Build.0 = Release|Any CPU
71 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|x86.ActiveCfg = Release|Any CPU
72 | {1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|x86.Build.0 = Release|Any CPU
73 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
75 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|x64.ActiveCfg = Debug|Any CPU
76 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|x64.Build.0 = Debug|Any CPU
77 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|x86.ActiveCfg = Debug|Any CPU
78 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|x86.Build.0 = Debug|Any CPU
79 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
80 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|Any CPU.Build.0 = Release|Any CPU
81 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|x64.ActiveCfg = Release|Any CPU
82 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|x64.Build.0 = Release|Any CPU
83 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|x86.ActiveCfg = Release|Any CPU
84 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|x86.Build.0 = Release|Any CPU
85 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
86 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|Any CPU.Build.0 = Debug|Any CPU
87 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|x64.ActiveCfg = Debug|Any CPU
88 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|x64.Build.0 = Debug|Any CPU
89 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|x86.ActiveCfg = Debug|Any CPU
90 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|x86.Build.0 = Debug|Any CPU
91 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Release|Any CPU.ActiveCfg = Release|Any CPU
92 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Release|Any CPU.Build.0 = Release|Any CPU
93 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Release|x64.ActiveCfg = Release|Any CPU
94 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Release|x64.Build.0 = Release|Any CPU
95 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Release|x86.ActiveCfg = Release|Any CPU
96 | {69DC1056-843E-4980-908A-5DB4ADA95460}.Release|x86.Build.0 = Release|Any CPU
97 | EndGlobalSection
98 | GlobalSection(SolutionProperties) = preSolution
99 | HideSolutionNode = FALSE
100 | EndGlobalSection
101 | GlobalSection(NestedProjects) = preSolution
102 | {9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5} = {AF454F92-8B84-446B-B0E2-9BA8887B09CC}
103 | {69DC1056-843E-4980-908A-5DB4ADA95460} = {AF454F92-8B84-446B-B0E2-9BA8887B09CC}
104 | EndGlobalSection
105 | GlobalSection(ExtensibilityGlobals) = postSolution
106 | SolutionGuid = {5B16EB0E-5342-4D93-9FA2-8327FF69A276}
107 | EndGlobalSection
108 | EndGlobal
109 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1.0.0
5 | latest
6 | enable
7 | true
8 | $(NoWarn);1591;1587;1574
9 |
10 |
11 | Write scripts with the power of .NET. Provides a shell script-like (bash, cmd, ...) experience to .NET application.
12 | Mayuki Sawatari
13 | Copyright © Mayuki Sawatari
14 | https://github.com/mayuki/Chell
15 | https://github.com/mayuki/Chell
16 |
17 | CommandLine Shell Process
18 | MIT
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright © Mayuki Sawatari
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Chell
2 | Write scripts with the power of C# and .NET.
3 |
4 | Chell is a library and execution tool for providing a shell script-like (bash, cmd, ...) experience to .NET applications.
5 |
6 | ```csharp
7 | var branch = await Run($"git branch --show-current");
8 | await Run($"git archive {branch} -o {branch}.zip");
9 | ```
10 |
11 | .NET applications are great for complex tasks, but executing processes can be boring. Chell brings the experience closer to shell scripting. This library is heavily influenced by [google/zx](https://github.com/google/zx).
12 |
13 | ## When should I use Chell?
14 | - **Write better shell scripts**: Write a complex script and use the power of .NET and C#
15 | - **Write multi-platform shell scripts**: As an alternative to scripts that work on multiple platforms
16 | - **Run a process quickly in your app**: As .NET library for easy handling of process launch and output
17 | - **All developers in the project are .NET developers**: 🙃
18 |
19 | Of course, if the shell script is already working fine and you don't have any problems, then there is no need to use Chell.
20 |
21 | ## Chell at a glance
22 | Using Chell makes the code feel more like a script by taking advantage of C# 9's top-level statements and C# 6's `using static`.
23 |
24 | ```csharp
25 | // Chell.Exports exposes a variety of functions and properties
26 | using Chell;
27 | using static Chell.Exports;
28 | ```
29 | ```csharp
30 | // Move the current directory with Cd method
31 | Cd("/tmp");
32 |
33 | // Dispose the return value of Cd method to return to the previous directory
34 | using (Cd("/usr/local/bin"))
35 | {
36 | // The current directory is "/usr/local/bin".
37 | }
38 | // The current directory is "/" again.
39 | ```
40 | ```csharp
41 | // You can run the process by passing a string to Run method
42 | await Run($"ls -lFa");
43 | ```
44 | ```csharp
45 | // An interpolated string passed to Run method will be escaped and expanded if it is an array
46 | var newDirs = new [] { "foo", "bar", "my app", "your;app" };
47 | await Run($"mkdir {newDirs}"); // $ mkdir foo bar "my app" "your;app"
48 | ```
49 | ```csharp
50 | // Run method returns the result object of the command (ProcessOutput class)
51 | var result = await Run($"ls -lFa");
52 | // You can read stdout & stderr line by line
53 | foreach (var line in result)
54 | {
55 | Echo(line);
56 | }
57 |
58 | // Allows to get stdout & stderr with implicit conversion to `string`
59 | string output = result;
60 | // You can also get stdout as bytes (ReadOnlyMemory)
61 | var binary = result.OutputBinary;
62 | ```
63 | ```csharp
64 | // Provides convenient extension methods for parsing JSON.
65 | var images = await Run($"docker image ls --format {"{{json .}}"}").SuppressConsoleOutputs();
66 | foreach (var image in images.AsJsonLines(new { Repository = "", ID = "", Tag = ""}))
67 | {
68 | Echo(image);
69 | }
70 | // $ docker image ls --format "{{json .}}"
71 | // { Repository = mcr.microsoft.com/dotnet/sdk, ID = b160c8f3dbd6, Tag = 5.0 }
72 | // { Repository = , ID = 3ee645b4a3bd, Tag = }
73 | ```
74 | ```csharp
75 | // Standard input/output of process tasks can be connected by pipes
76 | await (Run($"ls -lFa") | Run($"grep dotnet"));
77 | // The difference with `await (Run($"ls -lFa | grep dotnet"));` is that the shell can pipe or not.
78 |
79 | // You can also specify a Stream as input or output
80 | // Write ffmpeg output to a Stream.
81 | await (Run($"ffmpeg ...") | destinationStream);
82 | // Write a Stream to ffmpeg process.
83 | await (srcStream | Run($"ffmpeg ..."));
84 | ```
85 |
86 | Just want to make it easy for your app to handle processes? If you don't use `Chell.Exports`, you won't get any unnecessary methods or properties, and you'll get the same functions by `new Run(...)`.
87 |
88 | ```csharp
89 | using Chell;
90 | var result = await new Run($"ls -lF");
91 | ```
92 |
93 | Want to run it like a scripting language? Install [Chell.Run](#chellrun), and you can run it like a script.
94 |
95 | ```bash
96 | % dotnet tool install -g Chell.Run
97 |
98 | % chell -e "Echo(DateTime.Now)"
99 | 9/1/2021 0:00:00 PM
100 |
101 | % cat <<__EOF__ > MyScript.cs
102 | var dirs = new [] { "foo bar", "baz" };
103 | await Run($"mkdir {dirs}");
104 | await Run($"ls -l");
105 | __EOF__
106 |
107 | % chell MyScript.cs
108 | $ mkdir "foo bar" "baz"
109 | $ ls -l
110 | total 8
111 | drwxr-xr-x 2 mayuki mayuki 4096 Sep 1 00:00 baz/
112 | drwxr-xr-x 2 mayuki mayuki 4096 Sep 1 00:00 'foo bar'/
113 | ```
114 |
115 |
116 | ## Features
117 | - Automatic shell character escaping and array expansion
118 | - Stream and Process Pipes
119 | - Provide utilities and shortcuts useful for scripting.
120 | - Simple shell script-like execution tools
121 | - Multi-platform (Windows, Linux, macOS)
122 | - LINQPad friendly
123 |
124 | ## Install
125 | ```
126 | dotnet package add Chell
127 | ```
128 | ### Requirements
129 | .NET Standard 2.1, .NET 5 or higher
130 |
131 | ## Chell.Exports
132 | Chell.Exports class exposes a variety of utilities and shortcuts to make writing feel like shell scripting. It is recommended to include this class in your scripts with `static using`.
133 |
134 | ### Methods (Functions)
135 | #### `Run`
136 | Starts a process using the specified command-line and returns a `ProcessTask`.
137 |
138 | ```csharp
139 | await Run($"ls -lF");
140 |
141 | // The followings are equivalent to calling Run method
142 | await (Run)$"ls -lF";
143 | await new Run($"ls -lF");
144 | ```
145 |
146 | The process will be launched asynchronously and can wait for completion by `await`. And you can `await` to get a `ProcessOutput` object with its output.
147 |
148 | If the exit code of the process returns non-zero, it will throw an exception. To suppress this exception, see `NoThrow`.
149 |
150 | An interpolated string passed to Run method will be escaped and expanded if it is an array.
151 |
152 | ```csharp
153 | var newDirs = new [] { "foo", "bar", "my app", "your;app" };
154 | await Run($"mkdir {newDirs}"); // equivalent to `mkdir foo bar "my app" "your;app"`
155 | ```
156 |
157 | You can also pass an execution options (`ProcessTaskOptions`) to Run method.
158 |
159 | ```csharp
160 | await Run($"ping -t localhost", new ProcessTaskOptions(
161 | workingDirectory: @"C:\Windows",
162 | timeout: TimeSpan.FromSeconds(1)
163 | ));
164 | ```
165 |
166 | #### `Cd(string)`
167 | ```csharp
168 | Cd("/usr/local/bin"); // equivalent to `Environment.CurrentDirectory = "/usr/local/bin";`
169 | ```
170 |
171 | Dispose the return value of `Cd` method to return to the previous directory.
172 |
173 | ```csharp
174 | Cd("/"); // The current directory is "/".
175 | using (Cd("/usr/local/bin"))
176 | {
177 | // The current directory is "/usr/local/bin".
178 | }
179 | // The current directory is "/" again.
180 | ```
181 |
182 | #### `Mkdirp(string path)`
183 | Same as `mkdir -p`. Creates a new directory and any necessary sub-directories in the specified path.
184 |
185 | #### `Dump(T value)`
186 | Formats the object and write it to the console.
187 |
188 | ```csharp
189 | Dump(new { Foo = 123, Bar = "Baz" }); // => "{ Foo = 123, Bar = "Baz" }"
190 | ```
191 |
192 | #### `Which(string name)`, `TryWhich(string name, out string path)`
193 | Returns a path of the specified command.
194 |
195 | ```csharp
196 | var dotnetPath = Which("dotnet");
197 | await Run($"{dotnetPath} run");
198 | ```
199 |
200 | #### `Echo(object message = default)`
201 | `Echo` method is equivalent to Console.WriteLine.
202 |
203 | ```csharp
204 | Echo("Hello World!"); // equivalent to Console.WriteLine("Hello World!");
205 | ```
206 |
207 | #### `Sleep(int duration)`, `Sleep(TimeSpan duration)`
208 | Returns a Task that waits for the specified duration.
209 |
210 | ```csharp
211 | await Sleep(10); // Sleep for 10 seconds.
212 | ```
213 |
214 | #### `Exit(int exitCode)`
215 | Terminates the application with an exit code.
216 |
217 | ```csharp
218 | Exit(1);
219 | ```
220 |
221 | ### Properties
222 |
223 | #### `Env.Vars`
224 |
225 | Exposes the environment variables as `IDictionary`.
226 |
227 | ```csharp
228 | Env.Vars["PATH"] = Env.Vars["PATH"] + ":/path/to/";
229 | ```
230 |
231 | #### `Env.IsWindows`
232 | Returns whether the running operating system is Windows or not. If it returns `false`, the operating system is Linux or macOS.
233 |
234 | ```csharp
235 | if (Env.IsWindows) { /* Something to do for Windows */ }
236 | ```
237 |
238 | #### `Env.Shell`
239 | Specify explicitly which shell to use, or set to not use a shell.
240 |
241 | ```csharp
242 | Env.Shell.UseBash();
243 | Env.Shell.NoUseShell();
244 | Env.Shell.UseCmd();
245 | ```
246 |
247 | #### `Env.Verbosity`
248 | Sets or gets the output level when executing a command/process.
249 |
250 | - `Verbosity.All`: Displays both the command line and the output of the command
251 | - `Verbosity.CommandLine`: Displays the command line
252 | - `Verbosity.Output`: Displays the output of the command
253 | - `Verbosity.Silent`: No display
254 |
255 | #### `Env.ProcessTimeout`
256 |
257 | Sets the timeout for running the process. The default value is `0` (disabled).
258 |
259 | ```csharp
260 | Env.ProcessTimeout = TimeSpan.FromSecond(1);
261 |
262 | // OperationCanceledException will be thrown after 1s.
263 | await Run($"ping -t localhost");
264 | ```
265 |
266 | #### `Arguments`
267 | Gets the arguments passed to the current application.
268 |
269 | ```csharp
270 | // $ myapp foo bar baz => new [] { "foo", "bar", "baz" };
271 | foreach (var arg in Arguments) { /* ... */ }
272 | ```
273 | #### `CurrentDirectory`, `ExecutableDirectory`, `ExecutableName`, `ExecutablePath`
274 | Gets the current directory and the application directory or name or path.
275 |
276 | ```csharp
277 | // C:\> cd C:\Users\Alice
278 | // C:\Users\Alice> Downloads\MyApp.exe
279 |
280 | Echo(CurrentDirectory); // C:\Users\Alice
281 | Echo(ExecutableDirectory); // C:\Users\Alice\Downloads
282 | Echo(ExecutableName); // MyApp.exe
283 | Echo(ExecutablePath); // C:\Users\Alice\Downloads\MyApp.exe
284 | ```
285 |
286 | #### `HomeDirectory`
287 | Gets the path of the current user's home directory.
288 |
289 | ```csharp
290 | // Windows: C:/Users/
291 | // Linux: /home/
292 | // macOS: /Users/
293 | Echo(HomeDirectory);
294 | ```
295 |
296 | #### `StdIn`, `StdOut`, `StdErr`
297 | Provides the wrapper with methods useful for reading and writing to the standard input/output/error streams.
298 |
299 | ```csharp
300 | // Reads data from standard input.
301 | await StdIn.ReadToEndAsync();
302 |
303 | // Writes data to standard output or error.
304 | StdOut.WriteLine("FooBar");
305 | StdErr.WriteLine("Oops!");
306 | ```
307 |
308 | ## ProcessTask class
309 | Represents the execution task of the process started by `Run`.
310 |
311 |
312 | ### `Pipe`
313 | Connects the standard output of the process to another `ProcessTask` or `Stream`.
314 |
315 | ```csharp
316 | await (Run($"ls -lF") | Run($"grep .dll"));
317 |
318 | // The followings are equivalent to using '|'.
319 | var procTask1 = Run($"ls -lF");
320 | var procTask2 = Run($"grep .dll");
321 | procTask1.Pipe(procTask2);
322 | ```
323 |
324 | A `Stream` can also be passed to Pipe. If the ProcessTask has connected to the `Stream`, it will not write to `ProcessOutput`.
325 |
326 | ```csharp
327 | var memStream = new MemoryStream();
328 | await Run($"ls -lF").Pipe(memStream);
329 | ```
330 |
331 | ### `ConnectStreamToStandardInput`
332 | Connects the Stream to the standard input of the process. The method can be called only once before the process starts.
333 |
334 | ```csharp
335 | await (myStream | Run($"grep .dll"));
336 |
337 | // The followings are equivalent to using '|'.
338 | var procTask = Run($"grep .dll");
339 | procTask.ConnectStreamToStandardInput(myStream);
340 | ```
341 |
342 | ### `NoThrow`
343 | Suppresses exception throwing when the exit code is non-zero.
344 |
345 | ```csharp
346 | await Run($"AppReturnsExitCodeNonZero").NoThrow();
347 | ```
348 |
349 | ### `SuppressConsoleOutputs`
350 | Suppresses the writing of command execution results to the standard output.
351 |
352 | ```csharp
353 | // equivalent to "Env.Verbosity = Verbosity.Silent" or pipe to null.
354 | await Run($"ls -lF").SuppressConsoleOutputs();
355 |
356 | ```
357 | ### `ExitCode`
358 | Returns a `Task` to get the exit code of the process. This is equivalent to waiting for a `ProcessTask` with `NoThrow`.
359 |
360 | ```csharp
361 | var proc = Run($"ls -lF");
362 | if (await proc.ExitCode != 0)
363 | {
364 | ...
365 | }
366 |
367 | // equivalent to `(await Run($"ls -lF").NoThrow()).ExitCode`
368 | ```
369 |
370 | ## ProcessOutput class
371 | Provides the results of the process execution.
372 |
373 | ### `Combined`, `CombinedBinary`
374 | Gets the combined standard output and error as a string or byte array.
375 |
376 | ### `Output`, `OutputBinary`
377 | Gets the standard output as a string or byte array.
378 |
379 | ### `Error`, `ErrorBinary`
380 | Gets the standard error as a string or byte array.
381 |
382 | ### `AsLines(bool trimEnd = false)`, `GetEnumerator()`
383 | Gets the combined standard output and error as a per-line `IEnumerable`.
384 |
385 | ```csharp
386 | // equivalent to `foreach (var line in procOutput.AsLines())`
387 | foreach (var line in procOutput) { ... }
388 | ```
389 |
390 | ### `ToString()`
391 | The method equivalent to `Combined` property.
392 |
393 | ### `ExitCode`
394 | Gets the exit code of the process.
395 |
396 | ## Utilities and shortcuts
397 | Chell.Exports class also exposes a variety of useful utilities and shortcuts to libraries.
398 |
399 | ### `Prompt`
400 | Prompts the user for input and gets it.
401 |
402 | ```csharp
403 | var name = await Prompt("What's your name? ");
404 | ```
405 |
406 | ### `Chalk`: Kokuban: Terminal string styling
407 | Provides a shortcut to [mayuki/Kokuban](https://github.com/mayuki/Kokuban). You can easily style the text on the terminal.
408 |
409 | ```csharp
410 | // "Error: " will be colored.
411 | Echo((Chalk.Red + "Error: ") + "Something went wrong.");
412 | ```
413 |
414 | ### `Glob`
415 | Provides a shortcut to `Microsoft.Extensions.FileSystemGlobbing`.
416 |
417 | - `Glob(params string[] patterns)`
418 | - `Glob(string baseDir, string[] patterns)`
419 |
420 | ```csharp
421 | // Glob patterns starting with '!' will be treated as excludes.
422 | foreach (var path in Glob("**/*.cs", "!**/*.vb"))
423 | {
424 | ...
425 | }
426 | ```
427 |
428 | ### JSON serialize/deserialize (System.Text.Json)
429 | Provides shortcuts to `System.Text.Json`.
430 |
431 | - `ToJson(T obj)`
432 | ```csharp
433 | var obj = new { Name = "Alice", Age = 18 };
434 | var json = ToJson(obj);
435 | Echo(json); // {"Name":"Alice","Age":18}
436 | ```
437 |
438 | - `FromJson(string json)`
439 | - `FromJson(string json, T shape)`
440 | ```csharp
441 | var json = "{ \"foo\": 123 }";
442 | var obj = FromJson(json, new { Foo = 0 });
443 | Dump(obj); // { Foo = 123 }
444 | ```
445 |
446 | - `AsJson`
447 | - `AsJsonLines`
448 | ```csharp
449 | using Chell;
450 | var output = await Run($"docker image ls --format {"{{json .}}"}");
451 | foreach (var image in output.AsJsonLines(new { Repository = "", ID = "", Tag = ""}))
452 | {
453 | // ...
454 | }
455 | ```
456 | ```csharp
457 | using Chell;
458 | var output = await Run($"kubectl version --client -o json");
459 | var obj = output.AsJson(new { clientVersion = new { major = "", minor = "", gitVersion = "" } });
460 | Echo(obj); // { clientVersion = { major = 1, minor = 21, gitVersion = v1.21.2 } }
461 | ```
462 |
463 | ### HTTP acccess (System.Net.Http)
464 | Provides shortcuts to `System.Net.Http.HttpClient`.
465 |
466 | - `FetchAsync`
467 | - `FetchByteArrayAsync`
468 | - `FetchStreamAsync`
469 | - `FetchStringAsync`
470 |
471 | ## Chell as a Library
472 | Chell can also be used as a utility library to run processes.
473 |
474 | If you don't use `Chell.Exports`, you won't get any unnecessary methods or properties, and you can use `Run` and `ChellEnvironment`, `Exports` class.
475 |
476 | ```csharp
477 | using Chell;
478 |
479 | var results = await new Run($"ls -lF");
480 |
481 | // ChellEnvironment.Current is equivalent to `Env` on `Chell.Exports`.
482 | Console.WriteLine(ChellEnvironment.Current.ExecutablePath);
483 | Console.WriteLine(ChellEnvironment.Current.ExecutableName);
484 | Console.WriteLine(ChellEnvironment.Current.Arguments);
485 | Console.WriteLine(ChellEnvironment.Current.Vars["PATH"]);
486 | ```
487 |
488 | ## Chell.Run
489 | Chell.Run executes the input source code in an environment where Chell and some libraries are available.
490 |
491 | It does not perform any NuGet package resolution, so we recommend creating a typical C# project if you need to handle such complexities.
492 |
493 | ```
494 | $ dotnet tool install -g Chell.Run
495 | ```
496 | ```bash
497 | $ chell -e "Echo(123);"
498 | $ chell <
540 |
541 | Permission is hereby granted, free of charge, to any person obtaining a copy
542 | of this software and associated documentation files (the "Software"), to deal
543 | in the Software without restriction, including without limitation the rights
544 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
545 | copies of the Software, and to permit persons to whom the Software is
546 | furnished to do so, subject to the following conditions:
547 |
548 | The above copyright notice and this permission notice shall be included in all
549 | copies or substantial portions of the Software.
550 |
551 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
552 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
553 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
554 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
555 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
556 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
557 | SOFTWARE.
558 | ```
559 |
--------------------------------------------------------------------------------
/samples/GettingStarted.Basic.Unix/GettingStarted.Basic.Unix.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/GettingStarted.Basic.Unix/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using static Chell.Exports;
4 |
5 | // Starts a process.
6 | // The array will be expanded and the elements will be escaped
7 | var dirs = new[] { "/", "/usr", "/bin" };
8 | var results = await Run($"ls -l {dirs}");
9 |
10 | // Enumerates the results to retrieve the standard output line by line.
11 | foreach (var line in results)
12 | {
13 | Echo($"Result> {line}");
14 | }
15 | Echo();
16 |
17 | // Built-in Variables
18 | Echo((Chalk.Green + "ExecutableName: ") + string.Join(' ', ExecutableName));
19 | Echo((Chalk.Green + "ExecutableDirectory: ") + string.Join(' ', ExecutableDirectory));
20 | Echo((Chalk.Green + "Arguments: ") + string.Join(' ', Arguments));
21 | Echo((Chalk.Green + "CurrentDirectory: ") + string.Join(' ', CurrentDirectory));
22 | Echo();
23 |
24 | // Environment Variables
25 | Echo((Chalk.Green + "Env.Vars[\"PATH\"]: ") + Env.Vars["PATH"]);
26 | Echo();
27 |
28 | // Standard Input/Error as Stream + Utility methods.
29 | StdOut.WriteLine("Hello World!");
30 | StdErr.WriteLine("Hello World! (Error)");
31 | Echo();
32 |
33 | // Get the data from network and pipe it to the process
34 | await (await FetchByteArrayAsync("http://www.example.com/") | Run("grep title"));
35 |
36 |
37 | // Temporarily change the current directory.
38 | using (Cd("/"))
39 | {
40 | await Run($"dir");
41 | }
42 |
43 | Exit(1);
44 |
--------------------------------------------------------------------------------
/samples/GettingStarted.Basic.Windows/GettingStarted.Basic.Windows.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/samples/GettingStarted.Basic.Windows/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using Chell;
4 | using static Chell.Exports;
5 |
6 | // Starts a process.
7 | // The array will be expanded and the elements will be escaped
8 | var dirs = new[] { @"C:\Windows\Microsoft.NET", @"C:\Program Files" };
9 | var results = await Run($"dir {dirs}"); //
10 |
11 | // Enumerates the results to retrieve the standard output line by line.
12 | foreach (var line in results.Where(x => x.Contains("Windows")))
13 | {
14 | Echo($"Result> {line}");
15 | }
16 | Echo();
17 |
18 | // Built-in Variables
19 | Echo((Chalk.Green + "ExecutableName: ") + string.Join(' ', ExecutableName));
20 | Echo((Chalk.Green + "ExecutableDirectory: ") + string.Join(' ', ExecutableDirectory));
21 | Echo((Chalk.Green + "Arguments: ") + string.Join(' ', Arguments));
22 | Echo((Chalk.Green + "CurrentDirectory: ") + string.Join(' ', CurrentDirectory));
23 | Echo();
24 |
25 | // Environment Variables
26 | Echo((Chalk.Green + "Env.Vars[\"PATH\"]: ") + Env.Vars["PATH"]);
27 | Echo();
28 |
29 | // Standard Input/Error as Stream + Utility methods.
30 | StdOut.WriteLine("Hello World!");
31 | StdErr.WriteLine("Hello World! (Error)");
32 | Echo();
33 |
34 | // Get the data from network and pipe it to the process
35 | await (await FetchByteArrayAsync("http://www.example.com/") | Run("findstr title"));
36 |
37 | // Temporarily change the current directory.
38 | using (Cd("C:\\Users"))
39 | {
40 | await Run($"dir");
41 | }
42 |
43 | Exit(1);
44 |
--------------------------------------------------------------------------------
/src/.editorconfig:
--------------------------------------------------------------------------------
1 | ###############################
2 | # C# Nullability #
3 | ###############################
4 | [*.cs]
5 | # CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.
6 | dotnet_diagnostic.CS8618.severity = error
7 | # CS8604: Possible null reference argument.
8 | dotnet_diagnostic.CS8604.severity = error
9 | # CS8629: Nullable value type may be null.
10 | dotnet_diagnostic.CS8629.severity = error
11 | # CS8600: Converting null literal or possible null value to non-nullable type.
12 | dotnet_diagnostic.CS8600.severity = error
13 | # CS8603: Possible null reference return.
14 | dotnet_diagnostic.CS8603.severity = error
15 | # CS8610: Nullability of reference types in type of parameter doesn't match overridden member.
16 | dotnet_diagnostic.CS8610.severity = error
17 | # CS8625: Cannot convert null literal to non-nullable reference type.
18 | dotnet_diagnostic.CS8625.severity = error
19 | # CS8606: Possible null reference assignment to iteration variable
20 | dotnet_diagnostic.CS8606.severity = error
21 | # CS8602: Dereference of a possibly null reference.
22 | dotnet_diagnostic.CS8602.severity = error
23 | # CS8601: Possible null reference assignment.
24 | dotnet_diagnostic.CS8601.severity = error
25 | # CS8614: Nullability of reference types in type of parameter doesn't match implicitly implemented member.
26 | dotnet_diagnostic.CS8614.severity = error
--------------------------------------------------------------------------------
/src/Chell.Run/Chell.Run.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1;net5.0
6 |
7 | Tool to run C# code like a script. Provides a shell script-like (bash, cmd, ...) experience to .NET application.
8 | true
9 | chell
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/Chell.Run/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using Cocona;
10 | using Microsoft.CodeAnalysis;
11 | using Microsoft.CodeAnalysis.CSharp.Scripting;
12 | using Microsoft.CodeAnalysis.Scripting;
13 |
14 | namespace Chell.Run
15 | {
16 | partial class Program
17 | {
18 | static Task Main(string[] args)
19 | => CoconaLiteApp.RunAsync(args);
20 |
21 | public class RunCommandParameterSet : ICommandParameterSet
22 | {
23 | [Option("ref", new[] { 'r' }, Description = "Additional reference assembly")]
24 | [HasDefaultValue]
25 | public string[]? References { get; set; } = null;
26 |
27 | [Option("using", new[] { 'u' }, Description = "Additional `using` namespace")]
28 | [HasDefaultValue]
29 | public string[]? Usings { get; set; } = null;
30 |
31 | [Option('q')]
32 | [HasDefaultValue]
33 | public bool Silent { get; set; } = false;
34 | }
35 |
36 | [IgnoreUnknownOptions]
37 | [Command(Description = "Chell.Run: Run C# script instantly.")]
38 | public async Task RunAsync(
39 | RunCommandParameterSet runParams,
40 | [Option('e', Description = "A one-line program that can be run instantly.")] string? eval = default,
41 | [Argument(Description = "The path to a script file, or arguments to pass to the script")] string[]? filenameOrArgs = default
42 | )
43 | {
44 | var fileName = filenameOrArgs is {Length: > 0} ? filenameOrArgs[0] : null;
45 |
46 | // -e ".." or --eval "..."
47 | if (!string.IsNullOrEmpty(eval))
48 | {
49 | var args = Environment.GetCommandLineArgs();
50 | var index = Array.FindIndex(args, x => x == "-e" || x == "--eval");
51 | args = args.Skip(index + 2).ToArray();
52 | await RunScriptAsync("", Environment.CurrentDirectory, eval, args, runParams);
53 | }
54 | // Read a script from stdin.
55 | else if (fileName == "-" || (string.IsNullOrWhiteSpace(fileName) && Console.IsInputRedirected))
56 | {
57 | // Pass the strings as arguments after '-'.
58 | var args = Array.Empty();
59 | if (fileName == "-")
60 | {
61 | args = Environment.GetCommandLineArgs();
62 | var index = Array.IndexOf(args, "-");
63 | args = args.Skip(index + 1).ToArray();
64 | }
65 |
66 | using var reader = new StreamReader(Console.OpenStandardInput());
67 | var code = await reader.ReadToEndAsync();
68 | await RunScriptAsync("", Environment.CurrentDirectory, code, args, runParams);
69 | }
70 | else
71 | {
72 | if (string.IsNullOrWhiteSpace(fileName))
73 | {
74 | throw new CommandExitedException("Error: Specify the path or pass the script from standard input.", -1);
75 | }
76 | if (!File.Exists(fileName))
77 | {
78 | throw new CommandExitedException("Error: No such file or directory.", -1);
79 | }
80 |
81 | var ext = Path.GetExtension(fileName);
82 | if (ext == ".cs")
83 | {
84 | // Run .cs script file.
85 | var fullPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, fileName));
86 |
87 | var args = filenameOrArgs?.Skip(1).ToArray() ?? Array.Empty();
88 | await RunScriptAsync(fullPath, Path.GetDirectoryName(fullPath) ?? Environment.CurrentDirectory, await File.ReadAllTextAsync(fileName, Encoding.UTF8), args, runParams);
89 | }
90 | else
91 | {
92 | throw new CommandExitedException("Error: The specified file has unknown extension. Chell accepts a filename with `.cs` extension.", -1);
93 | }
94 | }
95 | }
96 |
97 | private async Task RunScriptAsync(string fileName, string executableDirectory, string content, string[] args, RunCommandParameterSet runParams)
98 | {
99 | _ = typeof(System.Text.Json.JsonSerializer).Assembly;
100 | _ = typeof(Chell.ChellEnvironment).Assembly;
101 | _ = typeof(Cocona.CoconaLiteApp).Assembly;
102 | _ = typeof(Sharprompt.Prompt).Assembly;
103 | _ = typeof(Mono.Options.Command).Assembly;
104 |
105 | var references = AppDomain.CurrentDomain.GetAssemblies()
106 | .Distinct()
107 | .GroupBy(x => x)
108 | .Select(x => x.Last())
109 | .Select(x => MetadataReference.CreateFromFile(x.Location));
110 | var usings = new[]
111 | {
112 | "System",
113 | "System.Collections",
114 | "System.Collections.Generic",
115 | "System.Diagnostics",
116 | "System.IO",
117 | "System.Text",
118 | "System.Text.RegularExpressions",
119 | "System.Linq",
120 | "System.Threading",
121 | "System.Threading.Tasks",
122 | "Chell",
123 | "Chell.Extensions",
124 |
125 | // using static
126 | "Chell.Exports"
127 | }.AsEnumerable();
128 |
129 | var scriptOptions = ScriptOptions.Default
130 | .AddImports(usings.Concat(runParams.Usings ?? Array.Empty()))
131 | .AddReferences(references)
132 | .AddReferences(runParams.References ?? Array.Empty());
133 |
134 | try
135 | {
136 | if (runParams.Silent)
137 | {
138 | ChellEnvironment.Current.Verbosity = ChellVerbosity.Silent;
139 | }
140 |
141 | ChellEnvironment.Current.SetCommandLineArgs(fileName, Path.GetFileName(fileName), executableDirectory, args);
142 | var script = await CSharpScript.RunAsync(content, scriptOptions);
143 | }
144 | catch (CompilationErrorException e)
145 | {
146 | Console.Error.WriteLine($"{fileName}{e.Message}");
147 | }
148 | catch (ProcessTaskException e)
149 | {
150 | Console.Error.WriteLine(e.Message);
151 | }
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/Chell/Chell.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.1;netcoreapp3.1;net5.0
5 | latest
6 | enable
7 | true
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | TextTemplatingFileGenerator
23 | ProcessTaskExtensions.Generated.cs
24 |
25 |
26 | TextTemplatingFileGenerator
27 | ChellWritableStream.Generated.cs
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | True
38 | True
39 | ProcessTaskExtensions.tt
40 |
41 |
42 | True
43 | True
44 | ChellWritableStream.tt
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/Chell/ChellEnvironment.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.IO.Pipes;
7 | using System.Linq;
8 | using System.Runtime.InteropServices;
9 | using System.Threading;
10 | using Chell.Internal;
11 | using Chell.IO;
12 | using Chell.Shell;
13 |
14 | namespace Chell
15 | {
16 | [Flags]
17 | public enum ChellVerbosity
18 | {
19 | ///
20 | /// By default, no output is written to the console.
21 | ///
22 | Silent = 0,
23 |
24 | ///
25 | /// Writes a executing command line to the console.
26 | ///
27 | CommandLine = 1 << 0,
28 |
29 | ///
30 | /// Writes a command output to the console.
31 | ///
32 | ConsoleOutputs = 1 << 1,
33 |
34 | ///
35 | /// Writes all command lines and command outputs.
36 | ///
37 | Full = CommandLine | ConsoleOutputs,
38 |
39 | [EditorBrowsable(EditorBrowsableState.Never)]
40 | Debug = Full | 1 << 31,
41 | }
42 |
43 | public class ChellEnvironment
44 | {
45 | public static ChellEnvironment Current { get; set; } = new ChellEnvironment();
46 |
47 | private string[] _arguments;
48 | private string? _executablePath;
49 | private string _executableName;
50 | private string _executableDirectory;
51 |
52 | public ChellEnvironment()
53 | {
54 | var args = Environment.GetCommandLineArgs();
55 | var path = args[0];
56 | _arguments = args.Skip(1).ToArray();
57 | _executablePath = path;
58 | _executableName = Path.GetFileName(path);
59 | _executableDirectory = Path.GetDirectoryName(path)!;
60 | }
61 |
62 | ///
63 | /// Gets or sets the verbosity.
64 | ///
65 | public ChellVerbosity Verbosity { get; set; } = ChellVerbosity.Full;
66 |
67 | public ShellExecutorProvider Shell { get; } = new ShellExecutorProvider();
68 | public IConsoleProvider Console { get; set; } =
69 | LINQPadHelper.RunningOnLINQPad
70 | ? new LINQPadConsoleProvider()
71 | : SystemConsoleProvider.Instance;
72 |
73 | ///
74 | /// Gets the identifier for the current application process.
75 | ///
76 | public int ProcessId =>
77 | #if NET5_0_OR_GREATER
78 | Environment.ProcessId
79 | #else
80 | Process.GetCurrentProcess().Id
81 | #endif
82 | ;
83 |
84 | ///
85 | /// Gets whether the current application is running on Windows.
86 | ///
87 | public bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
88 |
89 | ///
90 | /// Gets the command line arguments. like args of a entry point.
91 | ///
92 | public IReadOnlyList Arguments => _arguments;
93 |
94 | ///
95 | /// Gets the path of the executing application. like argv[0]. (e.g. C:\\Path\To\App.exe, /path/to/app)
96 | ///
97 | ///
98 | /// The path may be null when running a inline script.
99 | ///
100 | public string? ExecutablePath => _executablePath;
101 |
102 | ///
103 | /// Gets the name of the executing application. like argv[0]. (e.g. App.exe, app)
104 | ///
105 | public string ExecutableName => _executableName;
106 |
107 | ///
108 | /// Gets the directory of the executing application. like argv[0]. (e.g. C:\\Path\To, /path/to)
109 | ///
110 | public string ExecutableDirectory => _executableDirectory;
111 |
112 | ///
113 | /// Gets or sets the path of the current working directory.
114 | ///
115 | public string CurrentDirectory
116 | {
117 | get => Environment.CurrentDirectory;
118 | set => Environment.CurrentDirectory = value;
119 | }
120 |
121 | ///
122 | /// Gets the path of the current user's home directory.
123 | ///
124 | public string HomeDirectory => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
125 |
126 | ///
127 | /// Gets the environment variables as representation.
128 | ///
129 | public IDictionary Vars { get; } = new EnvironmentVariables();
130 |
131 | ///
132 | /// Gets the standard input stream.
133 | ///
134 | public ChellReadableStream StdIn => new ChellReadableStream(this.Console.OpenStandardInput(), this.Console.InputEncoding);
135 |
136 | ///
137 | /// Gets the standard output stream.
138 | ///
139 | public ChellWritableStream StdOut => new ChellWritableStream(this.Console.OpenStandardOutput(), this.Console.OutputEncoding);
140 |
141 | ///
142 | /// Gets the standard output stream.
143 | ///
144 | public ChellWritableStream StdErr => new ChellWritableStream(this.Console.OpenStandardError(), this.Console.OutputEncoding);
145 |
146 | ///
147 | /// Gets or sets the default timeout for the process. The value affects the current application. The default value is .
148 | ///
149 | ///
150 | /// If the value is or , the process will not be timed out.
151 | ///
152 | public TimeSpan ProcessTimeout { get; set; } = TimeSpan.Zero;
153 |
154 | [EditorBrowsable(EditorBrowsableState.Never)]
155 | public void SetCommandLineArgs(string? executablePath, string executableName, string executableDirectory, string[] args)
156 | {
157 | _arguments = args.ToArray();
158 | _executableName = executableName;
159 | _executablePath = executablePath;
160 | _executableDirectory = executableDirectory;
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/Chell/CommandLineString.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Diagnostics;
4 |
5 | namespace Chell
6 | {
7 | ///
8 | /// Workaround for string/FormattableString overload issues
9 | ///
10 | [EditorBrowsable(EditorBrowsableState.Never)]
11 | [DebuggerDisplay("CommandLineString: String={StringValue,nq}; FormattableString={FormattableStringValue,nq}")]
12 | public readonly struct CommandLineString
13 | {
14 | public string? StringValue { get; }
15 | public FormattableString? FormattableStringValue { get; }
16 |
17 | public CommandLineString(string value)
18 | {
19 | StringValue = value;
20 | FormattableStringValue = null;
21 | }
22 |
23 | public CommandLineString(FormattableString value)
24 | {
25 | StringValue = null;
26 | FormattableStringValue = value;
27 | }
28 |
29 | public static implicit operator CommandLineString(string value)
30 | {
31 | return new CommandLineString(value);
32 | }
33 |
34 | public static implicit operator CommandLineString(FormattableString value)
35 | {
36 | return new CommandLineString(value);
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/src/Chell/Exports.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.FileSystemGlobbing;
2 | using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Net.Http;
8 | using System.Text.Json;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 | using Chell.Internal;
12 | using Chell.IO;
13 | using Kokuban;
14 | using Kokuban.AnsiEscape;
15 |
16 | namespace Chell
17 | {
18 | public static class Exports
19 | {
20 | public static class Verbosity
21 | {
22 | ///
23 | /// By default, no output is written to the console.
24 | ///
25 | public const ChellVerbosity Silent = ChellVerbosity.Silent;
26 |
27 | ///
28 | /// Writes a executing command line to the console.
29 | ///
30 | public const ChellVerbosity CommandLine = ChellVerbosity.CommandLine;
31 |
32 | ///
33 | /// Writes a command output to the console.
34 | ///
35 | public const ChellVerbosity ConsoleOutputs = ChellVerbosity.ConsoleOutputs;
36 |
37 | ///
38 | /// Writes all command lines and command outputs.
39 | ///
40 | public const ChellVerbosity Full = ChellVerbosity.Full;
41 | }
42 |
43 | ///
44 | /// Gets the current environment.
45 | ///
46 | public static ChellEnvironment Env => ChellEnvironment.Current;
47 |
48 | ///
49 | /// Gets the standard input stream.
50 | ///
51 | public static ChellReadableStream StdIn => Env.StdIn;
52 |
53 | ///
54 | /// Gets the standard output stream.
55 | ///
56 | public static ChellWritableStream StdOut => Env.StdOut;
57 |
58 | ///
59 | /// Gets the standard error stream.
60 | ///
61 | public static ChellWritableStream StdErr => Env.StdErr;
62 |
63 | ///
64 | /// Gets the command line arguments. like args of a entry point.
65 | ///
66 | public static IReadOnlyList Arguments => Env.Arguments;
67 |
68 | ///
69 | /// Gets the path of the executing application. like argv[0]. (e.g. C:\\Path\To\App.exe, /path/to/app)
70 | ///
71 | ///
72 | /// The path may be null when running a inline script.
73 | ///
74 | public static string? ExecutablePath => Env.ExecutablePath;
75 |
76 | ///
77 | /// Gets the name of the executing application. like argv[0]. (e.g. App.exe, app)
78 | ///
79 | public static string ExecutableName => Env.ExecutableName;
80 |
81 | ///
82 | /// Gets the directory of the executing application. like argv[0]. (e.g. C:\\Path\To, /path/to)
83 | ///
84 | public static string ExecutableDirectory => Env.ExecutableDirectory;
85 |
86 | ///
87 | /// Gets or sets the path of the current working directory.
88 | ///
89 | public static string CurrentDirectory
90 | {
91 | get => Env.CurrentDirectory;
92 | set => Env.CurrentDirectory = value;
93 | }
94 |
95 | ///
96 | /// Gets the identifier for the current application process.
97 | ///
98 | public static int ProcessId => Env.ProcessId;
99 |
100 | ///
101 | /// Gets the Kokuban ANSI style builder to decorate texts.
102 | ///
103 | public static AnsiStyle Chalk => Kokuban.Chalk.Create(KokubanOptions.Default);
104 |
105 | ///
106 | /// Starts the process task with the specified command line.
107 | ///
108 | ///
109 | ///
110 | ///
111 | public static ProcessTask Run(FormattableString commandLine, ProcessTaskOptions? options = default)
112 | => new ProcessTask(commandLine, options);
113 |
114 | ///
115 | /// Starts the process task with the specified command line.
116 | ///
117 | ///
118 | ///
119 | ///
120 | public static ProcessTask Run(CommandLineString commandLine, ProcessTaskOptions? options = default)
121 | => new ProcessTask(commandLine, options);
122 |
123 | ///
124 | /// Starts the process task with the specified command line.
125 | ///
126 | /// The data to be passed to the standard input of the process.
127 | ///
128 | ///
129 | ///
130 | public static ProcessTask Run(Stream inputStream, FormattableString commandLine, ProcessTaskOptions? options = default)
131 | => new ProcessTask(inputStream, commandLine, options);
132 |
133 | ///
134 | /// Starts the process task with the specified command line.
135 | ///
136 | /// The data to be passed to the standard input of the process.
137 | ///
138 | ///
139 | ///
140 | public static ProcessTask Run(Stream inputStream, CommandLineString commandLine, ProcessTaskOptions? options = default)
141 | => new ProcessTask(inputStream, commandLine, options);
142 |
143 | ///
144 | /// Starts the process task with the specified command line.
145 | ///
146 | /// The data to be passed to the standard input of the process.
147 | ///
148 | ///
149 | ///
150 | public static ProcessTask Run(ReadOnlyMemory inputData, FormattableString commandLine, ProcessTaskOptions? options = default)
151 | => new ProcessTask(inputData, commandLine, options);
152 |
153 | ///
154 | /// Starts the process task with the specified command line.
155 | ///
156 | /// The data to be passed to the standard input of the process.
157 | ///
158 | ///
159 | ///
160 | public static ProcessTask Run(ReadOnlyMemory inputData, CommandLineString commandLine, ProcessTaskOptions? options = default)
161 | => new ProcessTask(inputData, commandLine, options);
162 |
163 | ///
164 | /// Writes the message to the console.
165 | ///
166 | ///
167 | public static void Echo(object? message = default)
168 | => ChellEnvironment.Current.Console.Out.WriteLine(message);
169 |
170 | ///
171 | /// Writes the object details to the console.
172 | ///
173 | ///
174 | ///
175 | public static void Dump(T obj)
176 | => ObjectDumper.Dump(obj);
177 |
178 | ///
179 | /// Converts the object to a JSON.
180 | ///
181 | ///
182 | public static string ToJson(T obj, JsonSerializerOptions? options = default)
183 | => JsonSerializer.Serialize(obj, options);
184 |
185 | ///
186 | /// Converts the JSON to an object.
187 | ///
188 | ///
189 | public static T? FromJson(string json, T shape)
190 | => FromJson(json);
191 |
192 | ///
193 | /// Converts the JSON to an object.
194 | ///
195 | ///
196 | public static T? FromJson(string json)
197 | => Chell.Extensions.StringExtensions.AsJson(json);
198 |
199 | ///
200 | /// Changes the current directory to the specified path.
201 | ///
202 | ///
203 | /// Dispose the return value to return to the previous directory.
204 | ///
205 | ///
206 | public static IDisposable Cd(string path)
207 | => new ChangeDirectoryScope(path);
208 |
209 | private class ChangeDirectoryScope : IDisposable
210 | {
211 | private readonly string _previousCurrentDirectory;
212 |
213 | public ChangeDirectoryScope(string newCurrentDirectory)
214 | {
215 | _previousCurrentDirectory = Environment.CurrentDirectory;
216 | ChangeDirectory(newCurrentDirectory);
217 | }
218 |
219 | public void Dispose()
220 | {
221 | ChangeDirectory(_previousCurrentDirectory);
222 | }
223 |
224 | private void ChangeDirectory(string path)
225 | {
226 | CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $"cd {path}");
227 | Environment.CurrentDirectory = path;
228 | }
229 | }
230 |
231 | ///
232 | /// Sleeps for the specified time.
233 | ///
234 | ///
235 | ///
236 | public static Task Sleep(TimeSpan timeSpan)
237 | => Task.Delay(timeSpan);
238 |
239 | ///
240 | /// Sleeps for the specified time.
241 | ///
242 | ///
243 | ///
244 | public static Task Sleep(int seconds)
245 | => Task.Delay(TimeSpan.FromSeconds(seconds));
246 |
247 | ///
248 | /// Get the task to ignore the exception and return .
249 | ///
250 | ///
251 | ///
252 | public static Task NoThrow(ProcessTask task)
253 | => task.NoThrow();
254 |
255 | ///
256 | /// Terminates the current process with specified exit code.
257 | ///
258 | ///
259 | public static void Exit(int exitCode = 0)
260 | => Environment.Exit(exitCode);
261 |
262 | ///
263 | /// Creates a new directory and any necessary sub-directories in the specified path.
264 | ///
265 | ///
266 | public static void Mkdirp(string path)
267 | => Directory.CreateDirectory(path);
268 |
269 | ///
270 | /// Fetches the content of the specified URL using GET method.
271 | ///
272 | ///
273 | ///
274 | ///
275 | public static Task FetchAsync(string requestUri, CancellationToken cancellationToken = default)
276 | {
277 | CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $"{nameof(FetchAsync)} {requestUri}");
278 | var httpClient = new HttpClient();
279 | return httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), cancellationToken);
280 | }
281 |
282 | ///
283 | /// Fetches the content of the specified URL as string using GET method.
284 | ///
285 | ///
286 | ///
287 | ///
288 | public static async Task FetchStringAsync(string requestUri, CancellationToken cancellationToken = default)
289 | {
290 | CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $"{nameof(FetchStringAsync)} {requestUri}");
291 | var httpClient = new HttpClient();
292 | var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), cancellationToken);
293 | res.EnsureSuccessStatusCode();
294 |
295 | #if NET5_0_OR_GREATER
296 | return await res.Content.ReadAsStringAsync(cancellationToken);
297 | #else
298 | return await res.Content.ReadAsStringAsync();
299 | #endif
300 | }
301 |
302 | ///
303 | /// Fetches the content of the specified URL as byte[] using GET method.
304 | ///
305 | ///
306 | ///
307 | ///
308 | public static async Task FetchByteArrayAsync(string requestUri, CancellationToken cancellationToken = default)
309 | {
310 | CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $"{nameof(FetchByteArrayAsync)} {requestUri}");
311 | var httpClient = new HttpClient();
312 | var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), cancellationToken);
313 | res.EnsureSuccessStatusCode();
314 |
315 | #if NET5_0_OR_GREATER
316 | return await res.Content.ReadAsByteArrayAsync(cancellationToken);
317 | #else
318 | return await res.Content.ReadAsByteArrayAsync();
319 | #endif
320 | }
321 |
322 | ///
323 | /// Fetches the content of the specified URL as Stream using GET method.
324 | ///
325 | ///
326 | ///
327 | ///
328 | public static async Task FetchStreamAsync(string requestUri, CancellationToken cancellationToken = default)
329 | {
330 | CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $"{nameof(FetchStreamAsync)} {requestUri}");
331 | var httpClient = new HttpClient();
332 | var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), cancellationToken);
333 | res.EnsureSuccessStatusCode();
334 |
335 | #if NET5_0_OR_GREATER
336 | return await res.Content.ReadAsStreamAsync(cancellationToken);
337 | #else
338 | return await res.Content.ReadAsStreamAsync();
339 | #endif
340 | }
341 |
342 | ///
343 | /// Gets the full path to a command, similar to the `which` command on Unix.
344 | ///
345 | ///
346 | ///
347 | public static string Which(string commandName)
348 | => Internal.Which.TryGetPath(commandName, out var matchedPath)
349 | ? matchedPath
350 | : throw new FileNotFoundException($"Command '{commandName}' is not found.");
351 |
352 | ///
353 | /// Gets the full path to a command, similar to the `which` command on Unix.
354 | ///
355 | ///
356 | ///
357 | ///
358 | public static bool TryWhich(string commandName, out string matchedPath)
359 | => Internal.Which.TryGetPath(commandName, out matchedPath);
360 |
361 | ///
362 | /// Enumerates paths under the current directory that match the specified glob pattern.
363 | ///
364 | ///
365 | /// A glob pattern accepts * and ** (e.g. **/*.cs). If the specify a pattern is started with '!', it will be treated as an excluded pattern.
366 | ///
367 | ///
368 | ///
369 | public static IEnumerable Glob(params string[] patterns)
370 | => Glob(Environment.CurrentDirectory, patterns);
371 |
372 | ///
373 | /// Enumerates paths under the specified directory that match the specified glob pattern.
374 | ///
375 | ///
376 | /// A glob pattern accepts * and ** (e.g. **/*.cs). If the specify a pattern is started with '!', it will be treated as an excluded pattern.
377 | ///
378 | ///
379 | ///
380 | ///
381 | public static IEnumerable Glob(string baseDir, string[] patterns)
382 | {
383 | var matcher = new Matcher();
384 |
385 | foreach (var pattern in patterns)
386 | {
387 | if (pattern.StartsWith("!"))
388 | {
389 | matcher.AddExclude(pattern.Substring(1));
390 | }
391 | else
392 | {
393 | matcher.AddInclude(pattern);
394 | }
395 | }
396 |
397 | var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(baseDir)));
398 | return result.Files
399 | .Select(x => Path.GetFullPath(Path.Combine(baseDir, x.Stem))); // NOTE: Microsoft.Extensions.FileSystemGlobbing 5.0.0 does not reflect the root directory in `Path`.
400 | }
401 |
402 | ///
403 | /// Displays the message and reads lines entered by the user from the console.
404 | ///
405 | ///
406 | ///
407 | public static async Task Prompt(string message)
408 | {
409 | Console.Write(message);
410 | return await Console.In.ReadLineAsync();
411 | }
412 | }
413 | }
414 |
--------------------------------------------------------------------------------
/src/Chell/Extensions/ChellExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Chell.Internal;
8 |
9 | namespace Chell.Extensions
10 | {
11 | public static class ChellExtensions
12 | {
13 | ///
14 | /// Writes a object details to the output.
15 | ///
16 | ///
17 | ///
18 | ///
19 | public static T Dump(this T value)
20 | {
21 | return ObjectDumper.Dump(value);
22 | }
23 |
24 | ///
25 | /// Writes a object details to the output.
26 | ///
27 | ///
28 | ///
29 | ///
30 | public static async Task Dump(this Task task)
31 | {
32 | var result = await task.ConfigureAwait(false);
33 | ObjectDumper.Dump(result);
34 | return result;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Chell/Extensions/ProcessOutputExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json;
3 |
4 | namespace Chell
5 | {
6 | public static class ProcessOutputExtensions
7 | {
8 | ///
9 | /// Enumerates lines by converting them to objects as JSON.
10 | ///
11 | ///
12 | /// Converts to the anonymous type specified in argument.
13 | ///
14 | public static IEnumerable AsJsonLines(this ProcessOutput processOutput, T shape, bool skipEmptyLine = true, JsonSerializerOptions? options = null)
15 | => Chell.Extensions.StringExtensions.AsJsonLines(processOutput.AsLines(), skipEmptyLine, options);
16 |
17 | ///
18 | /// Enumerates lines by converting them to objects as JSON.
19 | ///
20 | public static IEnumerable AsJsonLines(this ProcessOutput processOutput, bool skipEmptyLine = true, JsonSerializerOptions? options = null)
21 | => Chell.Extensions.StringExtensions.AsJsonLines(processOutput.AsLines(), skipEmptyLine, options);
22 |
23 | ///
24 | /// Converts the output string to an object as JSON.
25 | ///
26 | ///
27 | /// Converts to the anonymous type specified in argument.
28 | ///
29 | public static T? AsJson(this ProcessOutput processOutput, T shape, JsonSerializerOptions? options = null)
30 | => AsJson(processOutput, options);
31 |
32 | ///
33 | /// Converts the output string to an object as JSON.
34 | ///
35 | public static T? AsJson(this ProcessOutput processOutput, JsonSerializerOptions? options = null)
36 | => JsonSerializer.Deserialize(processOutput.ToString(), options);
37 | }
38 | }
--------------------------------------------------------------------------------
/src/Chell/Extensions/ProcessTaskExtensions.Generated.cs:
--------------------------------------------------------------------------------
1 | ///
2 | using System.Threading.Tasks;
3 |
4 | namespace Chell
5 | {
6 | public static partial class ProcessTaskExtensions
7 | {
8 | public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2)> GetAwaiter(this (ProcessTask T1, ProcessTask T2) tasks)
9 | {
10 | static async Task<(ProcessOutput Result1, ProcessOutput Result2)> WhenAllAsync(ProcessTask t1, ProcessTask t2)
11 | {
12 | var results = await Task.WhenAll(t1, t2).ConfigureAwait(false);
13 | return (results[0], results[1]);
14 | }
15 | return WhenAllAsync(tasks.T1, tasks.T2).GetAwaiter();
16 | }
17 | public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3) tasks)
18 | {
19 | static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3)
20 | {
21 | var results = await Task.WhenAll(t1, t2, t3).ConfigureAwait(false);
22 | return (results[0], results[1], results[2]);
23 | }
24 | return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3).GetAwaiter();
25 | }
26 | public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4) tasks)
27 | {
28 | static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4)
29 | {
30 | var results = await Task.WhenAll(t1, t2, t3, t4).ConfigureAwait(false);
31 | return (results[0], results[1], results[2], results[3]);
32 | }
33 | return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4).GetAwaiter();
34 | }
35 | public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5) tasks)
36 | {
37 | static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5)
38 | {
39 | var results = await Task.WhenAll(t1, t2, t3, t4, t5).ConfigureAwait(false);
40 | return (results[0], results[1], results[2], results[3], results[4]);
41 | }
42 | return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5).GetAwaiter();
43 | }
44 | public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6) tasks)
45 | {
46 | static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6)
47 | {
48 | var results = await Task.WhenAll(t1, t2, t3, t4, t5, t6).ConfigureAwait(false);
49 | return (results[0], results[1], results[2], results[3], results[4], results[5]);
50 | }
51 | return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6).GetAwaiter();
52 | }
53 | public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6, ProcessTask T7) tasks)
54 | {
55 | static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6, ProcessTask t7)
56 | {
57 | var results = await Task.WhenAll(t1, t2, t3, t4, t5, t6, t7).ConfigureAwait(false);
58 | return (results[0], results[1], results[2], results[3], results[4], results[5], results[6]);
59 | }
60 | return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6, tasks.T7).GetAwaiter();
61 | }
62 | public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6, ProcessTask T7, ProcessTask T8) tasks)
63 | {
64 | static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6, ProcessTask t7, ProcessTask t8)
65 | {
66 | var results = await Task.WhenAll(t1, t2, t3, t4, t5, t6, t7, t8).ConfigureAwait(false);
67 | return (results[0], results[1], results[2], results[3], results[4], results[5], results[6], results[7]);
68 | }
69 | return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6, tasks.T7, tasks.T8).GetAwaiter();
70 | }
71 | public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8, ProcessOutput Result9)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6, ProcessTask T7, ProcessTask T8, ProcessTask T9) tasks)
72 | {
73 | static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8, ProcessOutput Result9)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6, ProcessTask t7, ProcessTask t8, ProcessTask t9)
74 | {
75 | var results = await Task.WhenAll(t1, t2, t3, t4, t5, t6, t7, t8, t9).ConfigureAwait(false);
76 | return (results[0], results[1], results[2], results[3], results[4], results[5], results[6], results[7], results[8]);
77 | }
78 | return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6, tasks.T7, tasks.T8, tasks.T9).GetAwaiter();
79 | }
80 | public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8, ProcessOutput Result9, ProcessOutput Result10)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6, ProcessTask T7, ProcessTask T8, ProcessTask T9, ProcessTask T10) tasks)
81 | {
82 | static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8, ProcessOutput Result9, ProcessOutput Result10)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6, ProcessTask t7, ProcessTask t8, ProcessTask t9, ProcessTask t10)
83 | {
84 | var results = await Task.WhenAll(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10).ConfigureAwait(false);
85 | return (results[0], results[1], results[2], results[3], results[4], results[5], results[6], results[7], results[8], results[9]);
86 | }
87 | return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6, tasks.T7, tasks.T8, tasks.T9, tasks.T10).GetAwaiter();
88 | }
89 |
90 | }
91 | }
--------------------------------------------------------------------------------
/src/Chell/Extensions/ProcessTaskExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 |
3 | namespace Chell
4 | {
5 | public static partial class ProcessTaskExtensions
6 | {
7 | }
8 | }
--------------------------------------------------------------------------------
/src/Chell/Extensions/ProcessTaskExtensions.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | <#@ output extension=".Generated.cs" #>
7 | ///
8 | using System.Threading.Tasks;
9 |
10 | namespace Chell
11 | {
12 | public static partial class ProcessTaskExtensions
13 | {
14 | <# for (var i = 2; i <= 10; i++) {
15 | var inTuple = string.Join(", ", Enumerable.Range(1, i).Select(x => $"ProcessTask T{x}"));
16 | var outTuple = string.Join(", ", Enumerable.Range(1, i).Select(x => $"ProcessOutput Result{x}"));
17 | #>
18 | public static System.Runtime.CompilerServices.TaskAwaiter<(<#= outTuple #>)> GetAwaiter(this (<#= inTuple #>) tasks)
19 | {
20 | static async Task<(<#= outTuple #>)> WhenAllAsync(<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"ProcessTask t{x}")) #>)
21 | {
22 | var results = await Task.WhenAll(<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"t{x}")) #>).ConfigureAwait(false);
23 | return (<#= string.Join(", ", Enumerable.Range(0, i).Select(x => $"results[{x}]")) #>);
24 | }
25 | return WhenAllAsync(<#= string.Join(", ", Enumerable.Range(1, i).Select(x => $"tasks.T{x}")) #>).GetAwaiter();
26 | }
27 | <# } #>
28 |
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Chell/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.Json;
6 | using System.Threading.Tasks;
7 |
8 | namespace Chell.Extensions
9 | {
10 | public static class StringExtensions
11 | {
12 | ///
13 | /// Enumerates lines by converting them to objects as JSON.
14 | ///
15 | ///
16 | /// Converts to the anonymous type specified in argument.
17 | ///
18 | public static IEnumerable AsJsonLines(this IEnumerable lines, T shape, bool skipEmptyLine = true, JsonSerializerOptions? options = null)
19 | {
20 | return AsJsonLines(lines, skipEmptyLine, options);
21 | }
22 |
23 | ///
24 | /// Enumerates lines by converting them to objects as JSON.
25 | ///
26 | public static IEnumerable AsJsonLines(this IEnumerable lines, bool skipEmptyLine = true, JsonSerializerOptions? options = null)
27 | {
28 | return lines.Where(x => !skipEmptyLine || !string.IsNullOrWhiteSpace(x)).Select(x => JsonSerializer.Deserialize(x, options));
29 | }
30 |
31 | ///
32 | /// Converts the output string to an object as JSON.
33 | ///
34 | ///
35 | /// Converts to the anonymous type specified in argument.
36 | ///
37 | public static T? AsJson(this string json, T shape, JsonSerializerOptions? options = null)
38 | => AsJson(json, options);
39 |
40 | ///
41 | /// Converts the output string to an object as JSON.
42 | ///
43 | public static T? AsJson(this string json, JsonSerializerOptions? options = null)
44 | => JsonSerializer.Deserialize(json.ToString(), options);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Chell/IO/ChellWrappedStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace Chell.IO
11 | {
12 | public partial class ChellWritableStream : ChellWrappedStream
13 | {
14 | private readonly StreamWriter _writer;
15 |
16 | public ChellWritableStream(Stream baseStream, Encoding encoding)
17 | : base(baseStream)
18 | {
19 | _writer = new StreamWriter(baseStream, encoding);
20 | _writer.AutoFlush = true;
21 | }
22 |
23 | public void Write(byte[] value) => BaseStream.Write(value);
24 | public new void Write(ReadOnlySpan value) => BaseStream.Write(value);
25 | public ValueTask WriteAsync(byte[] value, CancellationToken cancellationToken = default) => BaseStream.WriteAsync(value, cancellationToken);
26 | public new ValueTask WriteAsync(ReadOnlyMemory value, CancellationToken cancellationToken = default) => BaseStream.WriteAsync(value, cancellationToken);
27 |
28 | protected override void Dispose(bool disposing)
29 | {
30 | _writer.Dispose();
31 | base.Dispose(disposing);
32 | }
33 |
34 | public override async ValueTask DisposeAsync()
35 | {
36 | await _writer.DisposeAsync();
37 | await base.DisposeAsync();
38 | }
39 | }
40 |
41 | public partial class ChellReadableStream : ChellWrappedStream
42 | {
43 | private readonly StreamReader _reader;
44 |
45 | public ChellReadableStream(Stream baseStream, Encoding encoding)
46 | : base(baseStream)
47 | {
48 | _reader = new StreamReader(baseStream, encoding);
49 | }
50 |
51 | public async Task ReadAllBytesAsync(CancellationToken cancellationToken = default)
52 | {
53 | var bufferWriter = new ArrayBufferWriter();
54 | while (true)
55 | {
56 | cancellationToken.ThrowIfCancellationRequested();
57 | var readLen = await BaseStream.ReadAsync(bufferWriter.GetMemory(1024 * 32), cancellationToken);
58 | if (readLen == 0)
59 | {
60 | return bufferWriter.WrittenMemory.ToArray();
61 | }
62 | bufferWriter.Advance(readLen);
63 | }
64 | }
65 |
66 | public byte[] ReadAllBytes()
67 | {
68 | var bufferWriter = new ArrayBufferWriter();
69 | while (true)
70 | {
71 | var readLen = BaseStream.Read(bufferWriter.GetSpan(1024 * 32));
72 | if (readLen == 0)
73 | {
74 | return bufferWriter.WrittenMemory.ToArray();
75 | }
76 | bufferWriter.Advance(readLen);
77 | }
78 | }
79 |
80 | public async Task ReadToEndAsync()
81 | {
82 | return await _reader.ReadToEndAsync();
83 | }
84 |
85 | public string ReadToEnd()
86 | {
87 | return _reader.ReadToEnd();
88 | }
89 |
90 | public IEnumerable ReadAllLines()
91 | {
92 | while (!_reader.EndOfStream)
93 | {
94 | var line = _reader.ReadLine();
95 | if (line is null) yield break;
96 |
97 | yield return line;
98 | }
99 | }
100 |
101 | public async IAsyncEnumerable ReadAllLinesAsync()
102 | {
103 | while (!_reader.EndOfStream)
104 | {
105 | var line = await _reader.ReadLineAsync();
106 | if (line is null) yield break;
107 |
108 | yield return line;
109 | }
110 | }
111 |
112 | protected override void Dispose(bool disposing)
113 | {
114 | _reader.Dispose();
115 | base.Dispose(disposing);
116 | }
117 | }
118 |
119 | public abstract class ChellWrappedStream : Stream
120 | {
121 | private readonly Stream _baseStream;
122 |
123 | protected Stream BaseStream => _baseStream;
124 |
125 | protected ChellWrappedStream(Stream baseStream)
126 | {
127 | _baseStream = baseStream;
128 | }
129 |
130 | #region Stream Implementation
131 | public override void Flush()
132 | {
133 | _baseStream.Flush();
134 | }
135 |
136 | public override int Read(byte[] buffer, int offset, int count)
137 | {
138 | return _baseStream.Read(buffer, offset, count);
139 | }
140 |
141 | public override int Read(Span buffer)
142 | {
143 | return _baseStream.Read(buffer);
144 | }
145 |
146 | public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
147 | {
148 | return _baseStream.ReadAsync(buffer, offset, count, cancellationToken);
149 | }
150 |
151 | public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = new CancellationToken())
152 | {
153 | return _baseStream.ReadAsync(buffer, cancellationToken);
154 | }
155 |
156 | public override long Seek(long offset, SeekOrigin origin)
157 | {
158 | return _baseStream.Seek(offset, origin);
159 | }
160 |
161 | public override void SetLength(long value)
162 | {
163 | _baseStream.SetLength(value);
164 | }
165 |
166 | public override void Write(byte[] buffer, int offset, int count)
167 | {
168 | _baseStream.Write(buffer, offset, count);
169 | }
170 |
171 | public override void Write(ReadOnlySpan buffer)
172 | {
173 | _baseStream.Write(buffer);
174 | }
175 |
176 | public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
177 | {
178 | return _baseStream.WriteAsync(buffer, offset, count, cancellationToken);
179 | }
180 |
181 | public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = new CancellationToken())
182 | {
183 | return _baseStream.WriteAsync(buffer, cancellationToken);
184 | }
185 |
186 | public override bool CanRead => _baseStream.CanRead;
187 |
188 | public override bool CanSeek => _baseStream.CanSeek;
189 |
190 | public override bool CanWrite => _baseStream.CanWrite;
191 |
192 | public override long Length => _baseStream.Length;
193 |
194 | public override long Position
195 | {
196 | get => _baseStream.Position;
197 | set => _baseStream.Position = value;
198 | }
199 | #endregion
200 | }
201 | }
202 |
--------------------------------------------------------------------------------
/src/Chell/IO/ChellWritableStream.Generated.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 | ///
3 | using System;
4 | using System.IO;
5 | using System.Threading.Tasks;
6 |
7 | namespace Chell.IO
8 | {
9 | public partial class ChellWritableStream
10 | {
11 | public void Write(string? value) => _writer.Write(value);
12 | public void WriteLine(string? value) => _writer.WriteLine(value);
13 | public Task WriteAsync(string? value) => _writer.WriteAsync(value);
14 | public Task WriteLineAsync(string? value) => _writer.WriteLineAsync(value);
15 | public void Write(char value) => _writer.Write(value);
16 | public void WriteLine(char value) => _writer.WriteLine(value);
17 | public Task WriteAsync(char value) => _writer.WriteAsync(value);
18 | public Task WriteLineAsync(char value) => _writer.WriteLineAsync(value);
19 | public void Write(char[]? value) => _writer.Write(value);
20 | public void WriteLine(char[]? value) => _writer.WriteLine(value);
21 | public Task WriteAsync(char[]? value) => _writer.WriteAsync(value);
22 | public Task WriteLineAsync(char[]? value) => _writer.WriteLineAsync(value);
23 | public void Write(object? value) => _writer.Write(value);
24 | public void WriteLine(object? value) => _writer.WriteLine(value);
25 | public void Write(double value) => _writer.Write(value);
26 | public void WriteLine(double value) => _writer.WriteLine(value);
27 | public void Write(float value) => _writer.Write(value);
28 | public void WriteLine(float value) => _writer.WriteLine(value);
29 | public void Write(long value) => _writer.Write(value);
30 | public void WriteLine(long value) => _writer.WriteLine(value);
31 | public void Write(int value) => _writer.Write(value);
32 | public void WriteLine(int value) => _writer.WriteLine(value);
33 | public void Write(ReadOnlySpan value) => _writer.Write(value);
34 | public void WriteLine(ReadOnlySpan value) => _writer.WriteLine(value);
35 | public Task WriteAsync(ReadOnlyMemory value) => _writer.WriteAsync(value);
36 | public Task WriteLineAsync(ReadOnlyMemory value) => _writer.WriteLineAsync(value);
37 | }
38 | }
--------------------------------------------------------------------------------
/src/Chell/IO/ChellWritableStream.tt:
--------------------------------------------------------------------------------
1 | <#@ template debug="false" hostspecific="false" language="C#" #>
2 | <#@ assembly name="System.Core" #>
3 | <#@ import namespace="System.Linq" #>
4 | <#@ import namespace="System.Text" #>
5 | <#@ import namespace="System.Collections.Generic" #>
6 | <#@ output extension=".Generated.cs" #>
7 | <#
8 | var types = new []
9 | {
10 | (0, "string?"),
11 | (0, "char"),
12 | (0, "char[]?"),
13 | (1, "object?"),
14 | (1, "double"),
15 | (1, "float"),
16 | (1, "long"),
17 | (1, "int"),
18 | (1, "ReadOnlySpan"),
19 | (2, "ReadOnlyMemory"),
20 | };
21 | #>
22 | #nullable enable
23 | ///
24 | using System;
25 | using System.IO;
26 | using System.Threading.Tasks;
27 |
28 | namespace Chell.IO
29 | {
30 | public partial class ChellWritableStream
31 | {
32 | <# foreach (var (target, type) in types) { #>
33 | <# if (target == 0 || target == 1) { #>
34 | public void Write(<#= type #> value) => _writer.Write(value);
35 | public void WriteLine(<#= type #> value) => _writer.WriteLine(value);
36 | <# } #>
37 | <# if (target == 0 || target == 2) { #>
38 | public Task WriteAsync(<#= type #> value) => _writer.WriteAsync(value);
39 | public Task WriteLineAsync(<#= type #> value) => _writer.WriteLineAsync(value);
40 | <# } #>
41 | <# } #>
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Chell/IO/IConsoleProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Text;
4 |
5 | namespace Chell.IO
6 | {
7 | public interface IConsoleProvider
8 | {
9 | Stream OpenStandardInput();
10 | Stream OpenStandardOutput();
11 | Stream OpenStandardError();
12 | Encoding InputEncoding { get; }
13 | Encoding OutputEncoding { get; }
14 | bool IsInputRedirected { get; }
15 | bool IsOutputRedirected { get; }
16 | bool IsErrorRedirected { get; }
17 |
18 | TextWriter Out { get; }
19 | TextWriter Error { get; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Chell/IO/LINQPadConsoleProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.IO.Pipelines;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Chell.IO
10 | {
11 | public class LINQPadConsoleProvider : IConsoleProvider
12 | {
13 | private readonly Pipe _pipe;
14 |
15 | public Encoding InputEncoding => Console.InputEncoding;
16 | public Encoding OutputEncoding => Console.OutputEncoding;
17 | public bool IsInputRedirected => Console.IsInputRedirected;
18 | public bool IsOutputRedirected => Console.IsOutputRedirected;
19 | public bool IsErrorRedirected => Console.IsErrorRedirected;
20 |
21 | public TextWriter Out { get; }
22 | public TextWriter Error { get; }
23 |
24 | public LINQPadConsoleProvider()
25 | {
26 | _pipe = new Pipe();
27 | Out = new PipeTextWriter(_pipe.Writer, OutputEncoding);
28 | Error = new PipeTextWriter(_pipe.Writer, OutputEncoding);
29 |
30 | var reader = new StreamReader(_pipe.Reader.AsStream());
31 | _ = Task.Run(async () =>
32 | {
33 | Memory buffer = new char[1024];
34 | while (true)
35 | {
36 | var read = await reader.ReadAsync(buffer, default);
37 | if (read != 0)
38 | {
39 | Console.Out.Write(buffer.Span.Slice(0, read));
40 | }
41 | }
42 | });
43 | }
44 |
45 | public Stream OpenStandardInput()
46 | => Console.OpenStandardInput();
47 |
48 | public Stream OpenStandardOutput()
49 | => _pipe.Writer.AsStream(leaveOpen: true);
50 |
51 | public Stream OpenStandardError()
52 | => _pipe.Writer.AsStream(leaveOpen: true);
53 |
54 | private class PipeTextWriter : TextWriter
55 | {
56 | private readonly PipeWriter _writer;
57 | public override Encoding Encoding { get; }
58 |
59 | public PipeTextWriter(PipeWriter writer, Encoding encoding)
60 | {
61 | _writer = writer;
62 | Encoding = encoding;
63 | }
64 |
65 | public override void Write(char value)
66 | {
67 | Span buffer = stackalloc byte[4];
68 | Span c = stackalloc char[1];
69 | c[0] = value;
70 |
71 | var written = Encoding.GetBytes(c, buffer);
72 | _writer.Write(buffer.Slice(0, written));
73 | _ = _writer.FlushAsync();
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Chell/IO/SystemConsoleProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 |
5 | namespace Chell.IO
6 | {
7 | ///
8 | /// Encapsulates console intrinsic members as object.
9 | ///
10 | public sealed class SystemConsoleProvider : IConsoleProvider
11 | {
12 | public static IConsoleProvider Instance { get; } = new SystemConsoleProvider();
13 |
14 | private SystemConsoleProvider()
15 | {}
16 |
17 | public Stream OpenStandardInput()
18 | => Console.OpenStandardInput();
19 |
20 | public Stream OpenStandardOutput()
21 | => Console.OpenStandardOutput();
22 |
23 | public Stream OpenStandardError()
24 | => Console.OpenStandardError();
25 |
26 | public Encoding InputEncoding => Console.InputEncoding;
27 | public Encoding OutputEncoding => Console.OutputEncoding;
28 |
29 | public bool IsInputRedirected => Console.IsInputRedirected;
30 | public bool IsOutputRedirected => Console.IsOutputRedirected;
31 | public bool IsErrorRedirected => Console.IsErrorRedirected;
32 |
33 | public TextWriter Out => Console.Out;
34 | public TextWriter Error => Console.Error;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Chell/Internal/CommandLineHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Linq;
4 | using Chell.IO;
5 | using Chell.Shell;
6 | using Kokuban;
7 |
8 | // ReSharper disable CoVariantArrayConversion
9 |
10 | namespace Chell.Internal
11 | {
12 | internal class CommandLineHelper
13 | {
14 | public static void WriteCommandLineToConsole(IConsoleProvider console, string commandLine, ChellVerbosity? verbosity = default)
15 | {
16 | verbosity ??= ChellEnvironment.Current.Verbosity;
17 | if (verbosity.Value.HasFlag(ChellVerbosity.CommandLine))
18 | {
19 | var parts = commandLine.Split(' ', 2);
20 | if (LINQPadHelper.RunningOnLINQPad)
21 | {
22 | LINQPadHelper.WriteRawHtml(
23 | $"$ {EscapeHtml(parts[0])}{EscapeHtml((parts.Length > 1 ? " " + parts[1] : string.Empty))}");
24 | }
25 | else
26 | {
27 | console.Out.WriteLine("$ " + (Chalk.BrightGreen + parts[0]) + (parts.Length > 1 ? " " + parts[1] : string.Empty));
28 | }
29 | }
30 |
31 | static string EscapeHtml(string s)
32 | => s.Replace("&", "&").Replace("\"", """).Replace("<", ">");
33 | }
34 |
35 | public static string Expand(FormattableString commandLine, IShellExecutor shellExecutor)
36 | {
37 | return string.Format(commandLine.Format.Trim(), commandLine.GetArguments().Select(x =>
38 | {
39 | return x switch
40 | {
41 | ProcessOutput procOutput => shellExecutor.Escape(procOutput.Output.TrimEnd('\n')),
42 | string s => shellExecutor.Escape(s),
43 | IEnumerable enumerable => string.Join(" ", enumerable.OfType