1 |
#Region "Microsoft.VisualBasic::be3eecb4606c697613eff4ec551101a2, Microsoft.VisualBasic.Core\CommandLine\CLITools.vb"
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
#End Region
|
46 |
|
47 |
Imports System.IO
|
48 |
Imports System.Runtime.CompilerServices
|
49 |
Imports System.Text
|
50 |
Imports System.Text.RegularExpressions
|
51 |
Imports Microsoft.VisualBasic.ApplicationServices.Terminal
|
52 |
Imports Microsoft.VisualBasic.CommandLine.Reflection
|
53 |
Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
|
54 |
Imports Microsoft.VisualBasic.Language
|
55 |
Imports Microsoft.VisualBasic.Linq
|
56 |
Imports Microsoft.VisualBasic.Scripting.MetaData
|
57 |
Imports Microsoft.VisualBasic.Text
|
58 |
Imports StringList = System.Collections.Generic.IEnumerable(Of String)
|
59 |
Imports ValueTuple = System.Collections.Generic.KeyValuePair(Of String, String)
|
60 |
|
61 |
Namespace CommandLine
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
<Package("CommandLine",
|
67 |
Url:="http://gcmodeller.org",
|
68 |
Publisher:="xie.guigang@gcmodeller.org",
|
69 |
Description:="",
|
70 |
Revision:=52)>
|
71 |
Public Module CLITools
|
72 |
|
73 |
<Extension>
|
74 |
Public Function ShellExec(cli As IIORedirectAbstract) As String
|
75 |
Call cli.Run()
|
76 |
Return cli.StandardOutput
|
77 |
End Function
|
78 |
|
79 |
<Extension>
|
80 |
Public Function Print(args As CommandLine, Optional sep As Char = " "c, Optional leftMargin% = 0) As String
|
81 |
Dim sb As New StringBuilder("ArgumentsOf: `" & args.Name & "`")
|
82 |
Dim device As New StringWriter(sb)
|
83 |
|
84 |
Call device.WriteLine()
|
85 |
Call device.WriteLine(New String("-"c, args.Name.Length * 4))
|
86 |
Call device.WriteLine()
|
87 |
|
88 |
Call args _
|
89 |
.ToArgumentVector _
|
90 |
.Print(
|
91 |
device,
|
92 |
sep,
|
93 |
trilinearTable:=True,
|
94 |
leftMargin:=leftMargin)
|
95 |
|
96 |
Return sb.ToString
|
97 |
End Function
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
<Extension> Public Function CreateParameterValues(tokens$(), IncludeLogicSW As Boolean, Optional note$ = Nothing) As List(Of NamedValue(Of String))
|
108 |
Dim list As New List(Of NamedValue(Of String))
|
109 |
|
110 |
If tokens.IsNullOrEmpty Then
|
111 |
Return list
|
112 |
ElseIf tokens.Length = 1 Then
|
113 |
|
114 |
If IsPossibleLogicFlag(tokens(Scan0)) AndAlso
|
115 |
IncludeLogicSW Then
|
116 |
|
117 |
list += New NamedValue(Of String) With {
|
118 |
.Name = tokens(Scan0),
|
119 |
.Value = CStr(True),
|
120 |
.Description = note
|
121 |
}
|
122 |
Else
|
123 |
Return list
|
124 |
End If
|
125 |
End If
|
126 |
|
127 |
|
128 |
|
129 |
For i As Integer = 0 To tokens.Length - 1
|
130 |
Dim [next] As Integer = i + 1
|
131 |
|
132 |
If [next] = tokens.Length Then
|
133 |
|
134 |
If IsPossibleLogicFlag(tokens(i)) AndAlso IncludeLogicSW Then
|
135 |
list += New NamedValue(Of String)(tokens(i), True, note)
|
136 |
End If
|
137 |
|
138 |
Exit For
|
139 |
End If
|
140 |
|
141 |
Dim s As String = tokens([next])
|
142 |
|
143 |
|
144 |
If IsPossibleLogicFlag(s) Then
|
145 |
If IncludeLogicSW Then
|
146 |
list += New NamedValue(Of String)(tokens(i), True, note)
|
147 |
End If
|
148 |
|
149 |
Continue For
|
150 |
Else
|
151 |
|
152 |
Dim key As String = tokens(i).ToLower
|
153 |
list += New NamedValue(Of String)(key, s, note)
|
154 |
|
155 |
i += 1
|
156 |
End If
|
157 |
Next
|
158 |
|
159 |
Return list
|
160 |
End Function
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
<Extension> Public Function GetLogicalFlags(args As IEnumerable(Of String), ByRef SingleValue$) As String()
|
169 |
Dim tokens$() = args.SafeQuery.ToArray
|
170 |
|
171 |
If tokens.IsNullOrEmpty Then
|
172 |
Return New String() {}
|
173 |
ElseIf tokens.Length = 1 Then
|
174 |
|
175 |
Return {tokens(0).ToLower}
|
176 |
End If
|
177 |
|
178 |
Dim tkList As New List(Of String)
|
179 |
|
180 |
For i As Integer = 0 To tokens.Length - 1
|
181 |
Dim next% = i + 1
|
182 |
|
183 |
If [next] = tokens.Length Then
|
184 |
If IsPossibleLogicFlag(obj:=tokens(i)) Then
|
185 |
tkList += tokens(i)
|
186 |
|
187 |
Exit For
|
188 |
End If
|
189 |
|
190 |
Dim s As String = tokens([next])
|
191 |
|
192 |
If IsPossibleLogicFlag(obj:=s) Then
|
193 |
If IsPossibleLogicFlag(obj:=tokens(i)) Then
|
194 |
tkList += tokens(i)
|
195 |
Else
|
196 |
|
197 |
If i = 0 Then
|
198 |
SingleValue = tokens(i)
|
199 |
End If
|
200 |
|
201 |
End If
|
202 |
Else
|
203 |
i += 1
|
204 |
End If
|
205 |
|
206 |
Next
|
207 |
|
208 |
Return (From s As String In tkList Select s.ToLower).ToArray
|
209 |
End Function
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
<ExportAPI("TryParse", Info:="Try parsing the cli command String from the String value.")>
|
220 |
<Extension>
|
221 |
Public Function TryParse(args As StringList,
|
222 |
Optional duplicatedAllows As Boolean = False,
|
223 |
Optional rawInput$ = Nothing) As CommandLine
|
224 |
|
225 |
Dim tokens$() = args.SafeQuery.ToArray
|
226 |
Dim singleValue$ = ""
|
227 |
|
228 |
If tokens.Length = 0 Then
|
229 |
Return New CommandLine
|
230 |
End If
|
231 |
|
232 |
Dim bools$() = tokens _
|
233 |
.Skip(1) _
|
234 |
.GetLogicalFlags(singleValue)
|
235 |
Dim cli As New CommandLine With {
|
236 |
.Name = tokens(Scan0).ToLower,
|
237 |
.Tokens = tokens,
|
238 |
.BoolFlags = bools,
|
239 |
.cliCommandArgvs = Join(tokens)
|
240 |
}
|
241 |
|
242 |
cli.SingleValue = singleValue
|
243 |
cli.cliCommandArgvs = rawInput
|
244 |
|
245 |
If cli.Parameters.Length = 1 AndAlso
|
246 |
String.IsNullOrEmpty(cli.SingleValue) Then
|
247 |
|
248 |
cli.SingleValue = cli.Parameters(0)
|
249 |
End If
|
250 |
|
251 |
If tokens.Length > 1 Then
|
252 |
cli.__arguments = tokens.Skip(1).ToArray.CreateParameterValues(False)
|
253 |
|
254 |
Dim Dk As String() = __checkKeyDuplicated(cli.__arguments)
|
255 |
|
256 |
If Not duplicatedAllows AndAlso Not Dk.IsNullOrEmpty Then
|
257 |
Dim Key$ = String.Join(", ", Dk)
|
258 |
Dim msg$ = String.Format(KeyDuplicated, Key, String.Join(" ", tokens.Skip(1).ToArray))
|
259 |
|
260 |
Throw New Exception(msg)
|
261 |
End If
|
262 |
End If
|
263 |
|
264 |
Return cli
|
265 |
End Function
|
266 |
|
267 |
Const KeyDuplicated As String = "The command line switch key ""{0}"" Is already been added! Here Is your input data: CMD {1}."
|
268 |
|
269 |
Private Function __checkKeyDuplicated(source As IEnumerable(Of NamedValue(Of String))) As String()
|
270 |
Dim LQuery = (From param As NamedValue(Of String)
|
271 |
In source
|
272 |
Select param.Name.ToLower
|
273 |
Group By ToLower Into Group).ToArray
|
274 |
|
275 |
Return LinqAPI.Exec(Of String) _
|
276 |
_
|
277 |
() <= From group
|
278 |
In LQuery
|
279 |
Where group.Group.Count > 1
|
280 |
Select group.ToLower
|
281 |
End Function
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
<ExportAPI("args", Info:="Gets the commandline object for the current program.")>
|
288 |
<MethodImpl(MethodImplOptions.AggressiveInlining)>
|
289 |
Public Function Args() As CommandLine
|
290 |
Return App.CommandLine
|
291 |
End Function
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 |
<ExportAPI("TryParse", Info:="Try parsing the cli command String from the String value.")>
|
304 |
Public Function TryParse(<Parameter("CLI", "The CLI arguments that inputs from the console by user.")> CLI$,
|
305 |
<Parameter("Duplicates.Allowed")> Optional duplicateAllowed As Boolean = False) As CommandLine
|
306 |
|
307 |
If String.IsNullOrEmpty(CLI) Then
|
308 |
Return New CommandLine
|
309 |
Else
|
310 |
#Const DEBUG = False
|
311 |
#If DEBUG Then
|
312 |
Call CLI.__DEBUG_ECHO
|
313 |
#End If
|
314 |
End If
|
315 |
|
316 |
Dim args As CommandLine = CLITools _
|
317 |
.GetTokens(CLI) _
|
318 |
.TryParse(duplicateAllowed, rawInput:=CLI)
|
319 |
|
320 |
Return args
|
321 |
End Function
|
322 |
|
323 |
|
324 |
Is this string tokens is a possible <see cref="Boolean"/> value flag
|
325 |
|
326 |
<param name="obj"></param>
|
327 |
|
328 |
<ExportAPI("IsPossibleBoolFlag?")>
|
329 |
Public Function IsPossibleLogicFlag(obj As String) As Boolean
|
330 |
If obj.Contains(" ") Then
|
331 |
Return False
|
332 |
End If
|
333 |
If IsNumeric(obj) Then
|
334 |
Return False
|
335 |
End If
|
336 |
|
337 |
|
338 |
If obj.Count("/"c) > 1 Then
|
339 |
Return False
|
340 |
End If
|
341 |
|
342 |
Return obj.StartsWith("-") OrElse
|
343 |
obj.StartsWith("/")
|
344 |
End Function
|
345 |
|
346 |
|
347 |
ReGenerate the cli command line argument string text.(重新生成命令行字符串)
|
348 |
|
349 |
<param name="tokens">If the token value have a space character, then this function will be wrap that token with quot character automatically.</param>
|
350 |
|
351 |
|
352 |
|
353 |
<ExportAPI("Join",
|
354 |
Info:="ReGenerate the cli command line argument string text.
|
355 |
NOTE: If the token have a space character, then this function will be wrap that token with quot character automatically.")>
|
356 |
Public Function Join(tokens As IEnumerable(Of String)) As String
|
357 |
If tokens Is Nothing Then
|
358 |
Return ""
|
359 |
Else
|
360 |
Return tokens _
|
361 |
.Select(AddressOf makesureQuot) _
|
362 |
.JoinBy(" ")
|
363 |
End If
|
364 |
End Function
|
365 |
|
366 |
Private Function makesureQuot(token As String) As String
|
367 |
If InStr(token, " ") > 0 Then
|
368 |
If token.First = """"c AndAlso token.Last = """"c Then
|
369 |
Return token
|
370 |
Else
|
371 |
Return $"""{token}"""
|
372 |
End If
|
373 |
Else
|
374 |
Return token
|
375 |
End If
|
376 |
End Function
|
377 |
|
378 |
|
379 |
A regex expression string that use for split the commandline text.
|
380 |
(用于分析命令行字符串的正则表达式)
|
381 |
|
382 |
|
383 |
Public Const SPLIT_REGX_EXPRESSION As String = "\s+(?=(?:[^""]|""[^""]*"")*$)"
|
384 |
|
385 |
|
386 |
Try parse the argument tokens which comes from the user input commandline string.
|
387 |
(尝试从用户输入的命令行字符串之中解析出所有的参数)
|
388 |
|
389 |
<param name="CLI"></param>
|
390 |
|
391 |
|
392 |
|
393 |
<ExportAPI("Tokens")>
|
394 |
Public Function GetTokens(CLI As String) As String()
|
395 |
If String.IsNullOrEmpty(CLI) Then
|
396 |
Return New String() {""}
|
397 |
Else
|
398 |
CLI = CLI.Trim
|
399 |
End If
|
400 |
|
401 |
|
402 |
|
403 |
|
404 |
|
405 |
|
406 |
|
407 |
|
408 |
|
409 |
' Return New String() {""}
|
410 |
|
411 |
|
412 |
|
413 |
Dim tokens$() = Regex.Split(CLI, SPLIT_REGX_EXPRESSION)
|
414 |
Dim argv As New List(Of String)
|
415 |
|
416 |
tokens = tokens _
|
417 |
.TakeWhile(Function(Token)
|
418 |
Return Not String.IsNullOrEmpty(Token.Trim)
|
419 |
End Function) _
|
420 |
.ToArray
|
421 |
|
422 |
For i As Integer = 0 To tokens.Length - 1
|
423 |
Dim s As String = tokens(i)
|
424 |
|
425 |
|
426 |
If s.First = ASCII.Quot AndAlso s.Last = ASCII.Quot Then
|
427 |
s = Mid(s, 2, Len(s) - 2)
|
428 |
End If
|
429 |
|
430 |
|
431 |
|
432 |
If s.Contains("="c) AndAlso Not s.IsURLPattern Then
|
433 |
If i > 0 AndAlso tokens(i - 1).TextEquals("/@set") Then
|
434 |
|
435 |
argv += s
|
436 |
Else
|
437 |
Call s.tupleParser(argv)
|
438 |
End If
|
439 |
Else
|
440 |
argv += s
|
441 |
End If
|
442 |
Next
|
443 |
|
444 |
Return argv
|
445 |
End Function
|
446 |
|
447 |
|
448 |
只取第一个=符号出现的位置,结果会被添加进入<paramref name="argv"/>列表之中
|
449 |
|
450 |
<param name="s$"></param>
|
451 |
<param name="argv"></param>
|
452 |
<Extension>
|
453 |
Private Sub tupleParser(s$, ByRef argv As List(Of String))
|
454 |
Dim splitIndex% = -1
|
455 |
|
456 |
For j As Integer = 0 To s.Length - 1
|
457 |
Dim c = s(j)
|
458 |
|
459 |
If c = "="c AndAlso splitIndex = -1 Then
|
460 |
|
461 |
If j > 1 AndAlso s(j - 1) <> "\"c Then
|
462 |
|
463 |
splitIndex = j
|
464 |
Exit For
|
465 |
Else
|
466 |
splitIndex = -1
|
467 |
End If
|
468 |
End If
|
469 |
Next
|
470 |
|
471 |
If splitIndex > -1 Then
|
472 |
Dim name$ = s.Substring(0, splitIndex)
|
473 |
Dim value = s.Substring(splitIndex + 1)
|
474 |
|
475 |
argv += name
|
476 |
argv += value
|
477 |
Else
|
478 |
argv += s
|
479 |
End If
|
480 |
End Sub
|
481 |
|
482 |
|
483 |
会对%进行替换的
|
484 |
|
485 |
Const TokenSplitRegex As String = "(?=(?:[^%]|%[^%]*%)*$)"
|
486 |
|
487 |
|
488 |
尝试从输入的语句之中解析出词法单元,注意,这个函数不是处理从操作系统所传递进入的命令行语句
|
489 |
|
490 |
<param name="CommandLine"></param>
|
491 |
|
492 |
|
493 |
|
494 |
<ExportAPI("TryParse")>
|
495 |
Public Function TryParse(CommandLine As String, TokenDelimited As String, InnerDelimited As Char) As String()
|
496 |
If String.IsNullOrEmpty(CommandLine) Then
|
497 |
Return New String() {""}
|
498 |
End If
|
499 |
|
500 |
Dim regxPattern$ = TokenDelimited & TokenSplitRegex.Replace("%"c, InnerDelimited)
|
501 |
Dim tokens = Regex.Split(CommandLine, regxPattern)
|
502 |
|
503 |
For i As Integer = 0 To tokens.Length - 1
|
504 |
Dim s As String = tokens(i)
|
505 |
|
506 |
If s.First = InnerDelimited AndAlso s.Last = InnerDelimited Then
|
507 |
tokens(i) = Mid(s, 2, Len(s) - 2)
|
508 |
End If
|
509 |
Next
|
510 |
|
511 |
Return tokens
|
512 |
End Function
|
513 |
|
514 |
|
515 |
Creates command line object from a set obj <see cref="KeyValuePair(Of String, String)"/>
|
516 |
|
517 |
<param name="name"></param>
|
518 |
<param name="args"></param>
|
519 |
<param name="bFlags"></param>
|
520 |
|
521 |
<ExportAPI("CreateObject")>
|
522 |
Public Function CreateObject(name$, args As IEnumerable(Of ValueTuple), Optional bFlags As IEnumerable(Of String) = Nothing) As CommandLine
|
523 |
Dim parameters As New List(Of NamedValue(Of String))
|
524 |
Dim tokens As New List(Of String) From {name}
|
525 |
|
526 |
For Each tuple As ValueTuple In args
|
527 |
Dim key As String = tuple.Key.ToLower
|
528 |
Dim param As New NamedValue(Of String)(key, tuple.Value)
|
529 |
|
530 |
Call parameters.Add(param)
|
531 |
Call tokens.AddRange(New String() {key, tuple.Value})
|
532 |
Next
|
533 |
|
534 |
Return New CommandLine With {
|
535 |
.Name = name,
|
536 |
.__arguments = parameters,
|
537 |
.Tokens = tokens.Join(bFlags).ToArray,
|
538 |
.BoolFlags = bFlags.SafeQuery.ToArray
|
539 |
}
|
540 |
End Function
|
541 |
|
542 |
|
543 |
Trim the CLI argument name its prefix symbols.
|
544 |
(修剪命令行参数名称的前置符号)
|
545 |
|
546 |
<param name="argName"></param>
|
547 |
|
548 |
<ExportAPI("Trim.Prefix.BoolFlag")>
|
549 |
<Extension>
|
550 |
Public Function TrimParamPrefix(argName$) As String
|
551 |
If argName.StartsWith("--") Then
|
552 |
Return Mid(argName, 3)
|
553 |
ElseIf argName.StartsWith("-") OrElse argName.StartsWith("\") OrElse argName.StartsWith("/") Then
|
554 |
Return Mid(argName, 2)
|
555 |
Else
|
556 |
Return argName
|
557 |
End If
|
558 |
End Function
|
559 |
|
560 |
|
561 |
请注意,这个是有方向性的,由于是依照参数1来进行比较的,假若args2里面的参数要多于第一个参数,但是第一个参数里面的所有参数值都可以被参数2完全比对得上的话,就认为二者是相等的
|
562 |
|
563 |
<param name="args1"></param>
|
564 |
<param name="args2"></param>
|
565 |
|
566 |
|
567 |
<ExportAPI("CLI.Equals")>
|
568 |
Public Function Equals(args1 As CommandLine, args2 As CommandLine) As Boolean
|
569 |
If Not String.Equals(args1.Name, args2.Name, StringComparison.OrdinalIgnoreCase) Then
|
570 |
Return False
|
571 |
End If
|
572 |
|
573 |
For Each bFlag As String In args1.BoolFlags
|
574 |
If Not args2.GetBoolean(bFlag) Then
|
575 |
Return False
|
576 |
End If
|
577 |
Next
|
578 |
|
579 |
For Each arg As NamedValue(Of String) In args1.__arguments
|
580 |
Dim value2 As String = args2(arg.Name)
|
581 |
|
582 |
If Not String.Equals(value2, arg.Value, StringComparison.OrdinalIgnoreCase) Then
|
583 |
Return False
|
584 |
End If
|
585 |
Next
|
586 |
|
587 |
Return True
|
588 |
End Function
|
589 |
|
590 |
<Extension>
|
591 |
Public Function SingleValueOrStdIn(args As CommandLine) As String
|
592 |
If Not String.IsNullOrEmpty(args.SingleValue) Then
|
593 |
Return args.SingleValue
|
594 |
Else
|
595 |
Dim reader As New StreamReader(Console.OpenStandardInput)
|
596 |
Return reader.ReadToEnd
|
597 |
End If
|
598 |
End Function
|
599 |
End Module
|
600 |
End Namespace
|