What is the %* or $* argument list equivalent for VBScript?
Asked Answered
M

2

9

Is there a %* (batch files) or $* (bash script) argument list equivalent for VBScript ?

I want to retrieve the exact command line invocation.

Contrived example:

cscript //nologo script.vbs /arg1:a -s "a b" 1 c /arg2:"x y" "d e" -l '3 4'

should return:

/arg1:a -s "a b" 1 c /arg2:"x y" "d e" -l '3 4'

(including the quotes).

I have looked at WScript.Arguments but it doesn't return the verbatim command line.

Mohur answered 28/6, 2015 at 2:30 Comment(0)
F
9

There is no equivalent to %* or $* in VBScript. The WScript.Arguments collection hides the input command line, giving access to the arguments as items inside collections.

The only way I know to retrieve the required information is to query WMI for the current process and from the process information read the command line.

This will give you the full command line used to start the current script.

Option Explicit

' We need a child process to locate the current script. 
Const FLAG_PROCESS = "winver.exe"

' WMI constants 
Const wbemFlagForwardOnly = 32

' Generate a unique value to be used as a flag
Dim guid
    guid = Left(CreateObject("Scriptlet.TypeLib").GUID,38)

' Start a process using the indicated flag inside its command line
    WScript.CreateObject("WScript.Shell").Run """" & FLAG_PROCESS & """ " & guid, 0, False

' To retrieve process information a WMI reference is needed    
Dim wmi
    Set wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")

' Query the list of processes with the flag in its command line, retrieve the 
' process ID of its parent process ( our script! ) and terminate the process 
Dim colProcess, process, myProcessID
    Set colProcess = wmi.ExecQuery( _ 
        "SELECT ParentProcessID From Win32_Process " & _ 
        "WHERE Name='" & FLAG_PROCESS & "' " & _ 
        "AND CommandLine LIKE '%" & guid & "%'" _ 
        ,"WQL" , wbemFlagForwardOnly _ 
    )
    For Each process In colProcess
        myProcessID = process.ParentProcessID
        process.Terminate
    Next 

' Knowing the process id of our script we can query the process list 
' and retrieve its command line
Dim commandLine
    set colProcess = wmi.ExecQuery( _ 
        "SELECT CommandLine From Win32_Process " & _ 
        "WHERE ProcessID=" & myProcessID _ 
        ,"WQL" , wbemFlagForwardOnly _ 
    )
    For Each process In colProcess
        commandLine = process.CommandLine
    Next 

' Done
    WScript.Echo commandLine
Ferocious answered 28/6, 2015 at 13:32 Comment(4)
This is a neat idea. But spawning and terminating a new process and running two WMI queries just seems like overkill vs just concatenating the strings in the WScript.Arguments collection.Triplett
@Bond, yes, it is not the most lightweight way to do it, but concatenating arguments will not see the difference between /test:"data" and /test:data or /test:"da""ta". Or /test:"da\"ta" and /test:"da\ta". Arguments collection will remove any double quotes, and OP stated that quotes should be included.Ferocious
You're right. Get's my vote. (Though OP should understand how args work in WSH and try to avoid those types of situations to begin with).Triplett
I agree that arguments should be both well formed and styled. I was looking for a way to log the command line verbatim for debugging purposes, which is useful for checking if users are doing dodgy things. It's also useful to know exactly what is entered and/or received. In this case, the VBS is invoked by a Nagios check_NRPE, which is then run by an agent on the client - so quote expansion can get complicated when crossing different shells.Mohur
S
1

There isn't one. But it's fairly trivial.

For each ag in wscript.arguments
    CMDLINE = CMDLINE & " " & ag
Next
wscript.echo mid(CMDLINE, 2)

or

For each ag in wscript.arguments
    If Instr(Ag, " ") = True then
        CMDLINE = CMDLINE & " " & Chr(34) & ag & Chr(34)
    Else
        CMDLINE = CMDLINE & " " & ag
    End if
Next
wscript.echo mid(CMDLINE, 2)

And

C:\Users\User>cscript //nologo "C:\Users\User\Desktop\New Text Document (3).vbs" cat dog "mouse and cat"
cat dog mouse and cat

This applies to VBScript and VBA.

Both of these basics are hosted by other programs. It is the host that collects commandline information (if any). It is the host that makes it available to vbs via an object in wscript's case, but not in when hosted in IE/IIS. And VBA has a host implemented Function (implemented by Corel Office, Microsoft's office, and VB6).

Function Declaration
Function Command() As Variant
Function Command$() As String
Runtime Semantics.
Returns the argument portion of the implementation dependent command used to initiate execution of the currently executing VBA program.
The runtime semantics of Command$ are identical to those of Command with the exception that the declared type of the return value is String rather than Variant.

Under the hood (I've removed no parsing behaviour paragraphs) (and note ANSI/Unicode differences)

CommandLineToArgvW Function


Parses a Unicode command-line string and returns an array of null-terminated Unicode strings containing the individual arguments found in that command line as well as a count of arguments, similar to the standard C run-time argv and argc values.

Syntax

LPWSTR *CommandLineToArgvW(          LPCWSTR lpCmdLine,
    int *pNumArgs
);

Parameters

This function accepts command lines containing a program name that is either enclosed in quotation marks or not enclosed in quotation marks.

CommandLineToArgvW has a special interpretation of backslash characters when they are followed by a quotation mark character ("), as follows:

  • 2n backslashes followed by a quotation mark produce n backslashes followed by a quotation mark.

    (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a quotation mark.

    n backslashes not followed by a quotation mark simply produce n backslashes.

GetCommandLine

Retrieves the command-line string for the current process.

LPTSTR WINAPI GetCommandLine(void);

ANSI console processes written in C can use the argc and argv arguments of the main function to access the command-line arguments. ANSI GUI applications can use the lpCmdLine parameter of the WinMain function to access the command-line string, excluding the program name. The reason that main and WinMain cannot return Unicode strings is that argc, argv, and lpCmdLine use the LPSTR data type for parameters, not the LPTSTR data type. The GetCommandLine function can be used to access Unicode strings, because it uses the LPTSTR data type.

To convert the command line to an argv style array of strings, call the CommandLineToArgvW function.

Note The name of the executable in the command line that the operating system provides to a process is not necessarily identical to that in the command line that the calling process gives to the CreateProcess function. The operating system may prepend a fully qualified path to an executable name that is provided without a fully qualified path.

Sollars answered 28/6, 2015 at 8:4 Comment(3)
Args with spaces need to be quoted.Joplin
Instr(Ag, " ") = True violates the rule "Never compare to True/False" (blogs.msdn.com/b/ericlippert/archive/2004/07/15/184431.aspx).Joplin
/arg2:"x y" "d e" becomes /arg2:x y and d e in the WScript.Arguments collection. Your code would then change it to "/arg2:x y" "d e". Not the same meaning.Triplett

© 2022 - 2024 — McMap. All rights reserved.