@@ -13,14 +13,23 @@ namespace Microsoft.Extensions.CommandLineUtils
13
13
{
14
14
internal class CommandLineApplication
15
15
{
16
- // Indicates whether the parser should throw an exception when it runs into an unexpected argument.
17
- // If this field is set to false , the parser will stop parsing when it sees an unexpected argument, and all
18
- // remaining arguments, including the first unexpected argument, will be stored in RemainingArguments property .
16
+ // Indicates whether the parser should throw an exception when it runs into an unexpected argument. If this is
17
+ // set to true (the default) , the parser will throw on the first unexpected argument. Otherwise, all unexpected
18
+ // arguments ( including the first) are added to RemainingArguments.
19
19
private readonly bool _throwOnUnexpectedArg ;
20
20
21
- public CommandLineApplication ( bool throwOnUnexpectedArg = true )
21
+ // Indicates whether the parser should check remaining arguments for command or option matches after
22
+ // encountering an unexpected argument. Ignored if _throwOnUnexpectedArg is true (the default). If
23
+ // _throwOnUnexpectedArg and this are both false, the first unexpected argument and all remaining arguments are
24
+ // added to RemainingArguments. If _throwOnUnexpectedArg is false and this is true, only unexpected arguments
25
+ // are added to RemainingArguments -- allowing a mix of expected and unexpected arguments, commands and
26
+ // options.
27
+ private readonly bool _continueAfterUnexpectedArg ;
28
+
29
+ public CommandLineApplication ( bool throwOnUnexpectedArg = true , bool continueAfterUnexpectedArg = false )
22
30
{
23
31
_throwOnUnexpectedArg = throwOnUnexpectedArg ;
32
+ _continueAfterUnexpectedArg = continueAfterUnexpectedArg ;
24
33
Options = new List < CommandOption > ( ) ;
25
34
Arguments = new List < CommandArgument > ( ) ;
26
35
Commands = new List < CommandLineApplication > ( ) ;
@@ -145,6 +154,7 @@ public int Execute(params string[] args)
145
154
{
146
155
shortOption = arg . Substring ( 1 ) . Split ( new [ ] { ':' , '=' } , 2 ) ;
147
156
}
157
+
148
158
if ( longOption != null )
149
159
{
150
160
processed = true ;
@@ -153,13 +163,27 @@ public int Execute(params string[] args)
153
163
154
164
if ( option == null )
155
165
{
156
- if ( string . IsNullOrEmpty ( longOptionName ) && ! command . _throwOnUnexpectedArg && AllowArgumentSeparator )
166
+ var ignoreContinueAfterUnexpectedArg = false ;
167
+ if ( string . IsNullOrEmpty ( longOptionName ) &&
168
+ ! command . _throwOnUnexpectedArg &&
169
+ AllowArgumentSeparator )
157
170
{
158
- // skip over the '--' argument separator
171
+ // Skip over the '--' argument separator then consume all remaining arguments. All
172
+ // remaining arguments are unconditionally stored for further use.
159
173
index ++ ;
174
+ ignoreContinueAfterUnexpectedArg = true ;
175
+ }
176
+
177
+ if ( HandleUnexpectedArg (
178
+ command ,
179
+ args ,
180
+ index ,
181
+ argTypeName : "option" ,
182
+ ignoreContinueAfterUnexpectedArg ) )
183
+ {
184
+ continue ;
160
185
}
161
186
162
- HandleUnexpectedArg ( command , args , index , argTypeName : "option" ) ;
163
187
break ;
164
188
}
165
189
@@ -191,6 +215,7 @@ public int Execute(params string[] args)
191
215
option = null ;
192
216
}
193
217
}
218
+
194
219
if ( shortOption != null )
195
220
{
196
221
processed = true ;
@@ -204,7 +229,11 @@ public int Execute(params string[] args)
204
229
205
230
if ( option == null )
206
231
{
207
- HandleUnexpectedArg ( command , args , index , argTypeName : "option" ) ;
232
+ if ( HandleUnexpectedArg ( command , args , index , argTypeName : "option" ) )
233
+ {
234
+ continue ;
235
+ }
236
+
208
237
break ;
209
238
}
210
239
@@ -268,6 +297,7 @@ public int Execute(params string[] args)
268
297
processed = true ;
269
298
}
270
299
}
300
+
271
301
if ( ! processed )
272
302
{
273
303
if ( arguments == null )
@@ -280,9 +310,14 @@ public int Execute(params string[] args)
280
310
arguments . Current . Values . Add ( arg ) ;
281
311
}
282
312
}
313
+
283
314
if ( ! processed )
284
315
{
285
- HandleUnexpectedArg ( command , args , index , argTypeName : "command or argument" ) ;
316
+ if ( HandleUnexpectedArg ( command , args , index , argTypeName : "command or argument" ) )
317
+ {
318
+ continue ;
319
+ }
320
+
286
321
break ;
287
322
}
288
323
}
@@ -490,17 +525,29 @@ public void ShowRootCommandFullNameAndVersion()
490
525
Out . WriteLine ( ) ;
491
526
}
492
527
493
- private void HandleUnexpectedArg ( CommandLineApplication command , string [ ] args , int index , string argTypeName )
528
+ private bool HandleUnexpectedArg (
529
+ CommandLineApplication command ,
530
+ string [ ] args ,
531
+ int index ,
532
+ string argTypeName ,
533
+ bool ignoreContinueAfterUnexpectedArg = false )
494
534
{
495
535
if ( command . _throwOnUnexpectedArg )
496
536
{
497
537
command . ShowHint ( ) ;
498
538
throw new CommandParsingException ( command , $ "Unrecognized { argTypeName } '{ args [ index ] } '") ;
499
539
}
540
+ else if ( _continueAfterUnexpectedArg && ! ignoreContinueAfterUnexpectedArg )
541
+ {
542
+ // Store argument for further use.
543
+ command . RemainingArguments . Add ( args [ index ] ) ;
544
+ return true ;
545
+ }
500
546
else
501
547
{
502
- // All remaining arguments are stored for further use
548
+ // Store all remaining arguments for later use.
503
549
command . RemainingArguments . AddRange ( new ArraySegment < string > ( args , index , args . Length - index ) ) ;
550
+ return false ;
504
551
}
505
552
}
506
553
0 commit comments