Double quotes in VBScript argument - vbscript

As I read and experienced for myself VBScript removes all double quotes from and argument. Does anyone know a way around this? How to pass double quotes into the script?

If that parameter requires quotes you could use a named parameter to identify it and then enclose the value with the double quotes
dim arg
if WScript.Arguments.Named.Exists("a") then
arg = WScript.Arguments.Named("a")
arg = chr(34) & arg & chr(34)
end if
and used thus:
cscript test.vbs /a:"a parameter"
but this doesn't help if you merely want to keep quotes if supplied. Single quotes are accepted though, so you could alternatively use single quotes (or another character/string) and do a Replace(arg, "'", chr(34)) to convert to double-quotes.

This script will get the command line as it is, with double quotes and everything to a variable called strLine, and display it:
Set objSWbemServices = GetObject("WinMgmts:Root\Cimv2")
Set colProcess = objSWbemServices.ExecQuery("Select * From Win32_Process")
For Each objProcess In colProcess
If InStr (objProcess.CommandLine, WScript.ScriptName) <> 0 Then
strLine = Mid(objProcess.CommandLine, InStr(objProcess.CommandLine , WScript.ScriptName) + Len(WScript.ScriptName) + 1)
End If
Next
WScript.Echo(strLine)
So running that with:
cscript scriptname.vbs "option" ""other option"" third option
would result in:
"option" ""other option"" third option

Rather than check for a command line to include WScript.ScriptName, you can get the current PID like in https://stackoverflow.com/a/13212628/1752986

Edit: Misunderstood the question so new answer here:
I don't think you can do that in any way. However, a work around might be to use the CommandLine property of the Win32_Process class, which should get you the complete commandline I think.
For example try this script:
Set wmi = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set processes = wmi.ExecQuery("SELECT * FROM Win32_Process")
For Each proc in processes
If InStr(proc.CommandLine, "double quotes") > 0 Then
wscript.echo proc.CommandLine
End IF
Next
With the parameters as: "some long commandline enclosed in double quotes here"

I am not sure if this works, but while passing parameter I guess you can do something like -
chr(34) + <your argument string> + chr(34)
The chr(34) stands for double quotes.

Unfortunately, I do not know any escape methods to pass double-quotes because its an argument delimiter. All I can suggest is to modify your script, or add the quotes from there.

Here's answer that draws upon/combines some of the others here and queries the process info based on the script's pid:
Function ArgumentsString()
Dim nPid : nPid = ThisProcessId()
For Each oPrc In GetObject("winmgmts:\\.\root\cimv2").ExecQuery(_
"Select * From Win32_Process Where ProcessId=" & nPid )
Exit For : Next
ArgumentsString = Mid( oPrc.CommandLine, _
InStr(oPrc.CommandLine, WScript.ScriptName) + _
Len(WScript.ScriptName) + 1 )
End Function
Function ThisProcessId()
ThisProcessId = 0
Dim sTFile, oPrc
With CreateObject("Scripting.FileSystemObject")
sTFile = .BuildPath(.GetSpecialFolder(2), "sleep.vbs")
With .OpenTextFile(sTFile, 2, True)
.Write "WScript.Sleep 1000"
End With
End With
With CreateObject("WScript.Shell").Exec("WScript " & sTFile)
For Each oPrc In GetObject("winmgmts:\\.\root\cimv2").ExecQuery(_
"Select * From Win32_Process Where ProcessId=" & .ProcessID)
Exit For : Next
ThisProcessId = oPrc.ParentProcessId
End With
End Function

Related

WshShell.Run of executable from UserProfile variable directory

I thought I had this correct but I don't. I'm trying to have an executable called from user profile directory. The variable is not expanding properly. This is the code example:
userProfilePath = oShell.ExpandEnvironmentStrings("%UserProfile%")
exePath = userProfilePath + "\test1.exe"
Set WshShell = WScript.CreateObject(""WScript.Shell"")
WshShell.Run """ & exePath & """
When the VBScript runs, it doesn't properly show the path of ("C:\Users\John Doe\test1.exe"). What am I missing on how to use the variable and string correctly?
Your quoting is incorrect. WScript.Shell must be in a single pair of double quotes, and if you want to add double quotes before and after the value of a variable you need to use either Chr(34)
WshShell.Run Chr(34) & exePath & Chr(34)
or sequences of 4 double quotes:
WshShell.Run """" & exePath & """"
The reason for the 4 consecutive double quotes is that VBScript string literals must begin and end with a double quote, and nested double quotes inside a string literal must be escaped by doubling them (so they don't prematurely terminate the string). Your literal """ & exePath & """ defines the literal text " & exePath & ", not the value of the variable exePath between two double qoutes.
With that said, in your particular case you don't need the whole concatenation shebang, because the Run method can handle environment variables by itself. The following code should suffice for your needs:
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run """%USERPROFILE%\test1.exe"""

VBScript how to join WScript.Arguments?

I am trying to join the arguments to a string to be passed to another script. The following:
WScript.Echo(Join(WScript.Arguments))
gives me an error:
Error: Wrong number of arguments or invalid property assignment
Code: 800A01C2
What is wrong with that syntax?
WshArgument objects are not arrays, so you can't use Join() on them. What you can do is something like this:
ReDim arr(WScript.Arguments.Count-1)
For i = 0 To WScript.Arguments.Count-1
arr(i) = WScript.Arguments(i)
Next
WScript.Echo Join(arr)
Another solution can be done with ArrayList object from the system:
Set oAL = CreateObject("System.Collections.ArrayList")
For Each oItem In Wscript.Arguments: oAL.Add oItem: Next
WScript.Echo Join(oAL.ToArray, " ")
ReDim arr(WScript.Arguments.Count-1)
For i = 0 To WScript.Arguments.Count-1
arr(i) = """"+WScript.Arguments(i)+""""
Next
WScript.Echo Join(arr)
this will add quotes for each argument,
you can then remove it in the batch file with %~1 and so on.
Here is the function that I use. It will return all the arguments whether they contain quotes or not in a string that you can pass to another script.
Function GetArguments()
Dim Args, Arg
If WSH.Arguments.Count > 0 Then
For Each Arg In WSH.Arguments
Args = Args & """" & Arg & """ "
Next
Args = " """"" & Trim(Args) & """"""
End If
GetArguments = Args
End Function

Passing-variable-from-vbscript-to-batch-file with arguments

Please how to pass the 'inp" variable from this piece of vbs to my batch named job.bat? Indeed when doing echoing (echo %2) from job.bat, i notice that the inp is not passed properly. prompt command views inp and not the value retrieved from vbs. Thanks
For Each listElement In xmlDoc.selectNodes("document/Lists/list")
msgbox "toto"
inp=listElement.selectSingleNode("entry").text
out= listElement.selectSingleNode("output").text
jeton= listElement.selectSingleNode("token").text
dim shell
set shell=createobject("wscript.shell")
shell.run "job.bat ""a file"" **inp** "
set shell=nothing
Next
I think what you're looking for is this.
shell.run "job.bat ""argfile.ext"" " & inp
However, as Ansgar Wiechers points out, this is a potentially severe security hole, as a treacherously crafted XML file could run arbitrary commands. To encapsulate your batch file arguments and prevent unintended consequences, consider switching to the Shell.Application object's ShellExecute method.
For Each listElement In xmlDoc.selectNodes("document/Lists/list")
msgbox "toto"
inp = listElement.selectSingleNode("entry").text
out = listElement.selectSingleNode("output").text
jeton = listElement.selectSingleNode("token").text
set shell=CreateObject("Shell.Application")
shell.ShellExecute "job.bat", """a file"" " & inp, "path\to\batfile\", "runas", 1
set shell=nothing
Next
Unlike several other languages VBScript doesn't expand variables inside strings. Because of that, inp in the string
"job.bat ""a file"" inp "
is just the literal string "inp", not the value of the variable inp. To produce a string with the value of a variable, you have to concatenate base string and variable like #rojo suggested:
shell.run "job.bat ""a file"" " & inp
I would, however, not recommend doing this without some safety precautions. For one thing you should always put double quotes around your arguments, in case they contain spaces. I normally use a quoting function for this to prevent the instruction from becoming riddled with quad-quotes:
Function qq(str) : qq = Chr(34) & str & Chr(34) : End Function
'...
shell.run "job.bat " & qq("a file") & " " & qq(inp)
You should also always apply sanitizing to all user input that is passed to a shell command. Otherwise your users might wreak havoc by entering something like foo & del /s /q C:\*.*. Common practice is to allow only known-good characters in the input string and replace everything else with a safe character (e.g. an underscore). You can achieve this with a regular expression:
Set re = New RegExp
re.Pattern = "[^ a-z0-9äöü.,_$%()-]"
re.Global = True
re.IgnoreCase = True
inp = re.Replace(inp, "_")

How to add double quotes in a string literal

Example code:
Dim a As String
a = 1234,5678,9123
I want to add literal double quotes to the variable a
Expected Output:
a = "1234,5678,9123"
How do I format the string so when I print it, it has double quotes around it?
If you want to include " in a string, supply "" where you want the quote to appear. So your example should read...
a = """1234,5678,9123"""
The current answers are correct and valid but sometimes the following can improve readability:
a = Chr$(34) & "1234,5678,9123" & Chr$(34)
To make Chr$(34) more readable:
Dim quote as string
quote = Chr$(34)
a = quote & "1234,5678,9123" & quote
This makes it easier to get the correct number of " symbols everywhere and is readable.
a = """1234,5678,9123"""
or
a= """" & a & """"
You just use Chr$(34) to insert a double quotes.
Eg:
Dim i as String
i = Chr$(34) & "Hello World" & Chr$(34) 'Shows "Hello World"
No need to add any kind of complicated functions just use following example to insert double in text box or rich text box.
Dim dquot=""""
TextBox1.AppendText("Hello " &dquot &"How are you ?" &quot)
or
Dim dquot=""""
RichTextBox1.AppendText("Hello " &dquot &"How are you ?" &quot)
I used the Chr$(34) method, like this:
Sub RunPython()
Dim scriptName As String
Dim stAppName As String
scriptName = ActiveWorkbook.Path & "\aprPlotter.py"
stAppName = "python.exe " & Chr$(34) & scriptName & Chr$(34)
Debug.Print stAppName
Call Shell(stAppName, vbHide)
End Sub
I was using the same path for the python script as the Excel Workbook, to keep it easier for the users. I monkeyed with the quotes for 45 minutes before I found this thread. When the paths are sourced from active working locations (especially ones with spaces in Windows), I think this is a preferred method. Hopefully, this will help someone else.

Force a VBS to run using cscript instead of wscript

What is the stackoverflow approved (and hence correct) method to force a VBS to run using cscript instead of wscript - irrespective of what the user tries?
A quick Google search shows plenty of examples, but some of them simply don't work and those which do often don't handle the fact that it may have been run with arguments so I'm keen to know what the best way is.
Here is one example which doesn't handle arguments:
sExecutable = LCase(Mid(Wscript.FullName, InstrRev(Wscript.FullName,"\")+1))
If sExecutable <> "cscript.exe" Then
Set oShell = CreateObject("wscript.shell")
oShell.Run "cscript.exe """ & Wscript.ScriptFullName & """"
Wscript.Quit
End If
I appreciate that this could probably be easily modified to handle arguments, but realise that this may not be the best way to approach the problem.
Background: I'm writing a script which can run by double clicking or (most likely) from either a DOS batch file or as a scheduled task. It can contain one or more optional command line arguments.
My Lord, what unadulterated rubbish. It makes me cry to see such cruddy coding (no offense to anybody, lol). Seriously, though, here's my 2 pence:
Sub forceCScriptExecution
Dim Arg, Str
If Not LCase( Right( WScript.FullName, 12 ) ) = "\cscript.exe" Then
For Each Arg In WScript.Arguments
If InStr( Arg, " " ) Then Arg = """" & Arg & """"
Str = Str & " " & Arg
Next
CreateObject( "WScript.Shell" ).Run _
"cscript //nologo """ & _
WScript.ScriptFullName & _
""" " & Str
WScript.Quit
End If
End Sub
forceCScriptExecution
It handles arguments, AND checks for spaces in said arguments -- so that in the case of a filename passed to the original script instance that contained spaces, it wouldn't get "tokenized" when passed to cscript.exe.
Only thing it doesn't do is test for StdIn (e.g., in the case where someone piped something to the script via the command line, but forgot to use "cscript script.vbs") -- but if it was executed by WScript.exe, WScript.StdIn's methods all return Invalid Handle errors, so there's no way to test that anyway.
Feel free to let me know if there's a way to "break" this; I'm willing to improve it if necessary.
Two small additions to forceCScriptExecution let me see its Window after termination and handle its return code.
Sub forceCScriptExecution
Dim Arg, Str
If Not LCase( Right( WScript.FullName, 12 ) ) = "\cscript.exe" Then
For Each Arg In WScript.Arguments
If InStr( Arg, " " ) Then Arg = """" & Arg & """"
Str = Str & " " & Arg
Next
**ret =** CreateObject( "WScript.Shell" ).Run **("cmd /k** cscript //nologo """ & WScript.ScriptFullName & """ " & Str**,1,true)**
WScript.Quit **ret**
End If
End Sub
Notes: "cmd /k" let the windows stay after execution. Parameter "1" activates the window. Parameter "true" waits for termination, so variable "ret" can return the error code.
Here's a similar one in JScript for making .js files run in CScript:
(function(ws) {
if (ws.fullName.slice(-12).toLowerCase() !== '\\cscript.exe') {
var cmd = 'cscript.exe //nologo "' + ws.scriptFullName + '"';
var args = ws.arguments;
for (var i = 0, len = args.length; i < len; i++) {
var arg = args(i);
cmd += ' ' + (~arg.indexOf(' ') ? '"' + arg + '"' : arg);
}
new ActiveXObject('WScript.Shell').run(cmd);
ws.quit();
}
})(WScript);
WScript.echo('We are now in CScript. Press Enter to Quit...');
WScript.stdIn.readLine();
https://gist.github.com/4482361
One approach might be to give it another extension instead of .vbs. Say .cvbs for example. Associate .cvbs with cscript.exe not wscript.exe, that way executing or double clicking a .cvbs file will never invoke the wscript.exe.
Here is my code snippet i use for some of my scripts. It handles Arguments as well. All you have to do is replace the {EnterWorC} with either a "w" or "c" WITH quotes
Dim WorC, Command, Arguments, I
WorC={EnterWOrC} 'Make sure you replace "{EnterWOrC}" with a "w" or a "c" and BE SURE TO PUT QUOTES AROUND THE LETTER.
WorC=LCase (WorC)
If lcase (WorC)="w" Or lcase (WorC)="c" Then
If LCase (Right (WScript.FullName,11))<> WorC & "script.exe" Then
command=WScript.ScriptFullName
Arguments=""
For I=0 To UBound (WScript.Arguments)
Arguments=Arguments & Chr (34) & WScript.Arguments(I) & Chr (34) & Space (1)
Next
CreateObject("Wscript.Shell").Run WorC & "script.exe " & Chr (34) & command & Chr (34) & Space (1) & Arguments, 1
WScript.Quit
End If
WorC=Empty
Command=Empty
I=Empty
Arguments=Empty
End If
Here you will have to replace the 2nd line (2nd NON-blank line)
WorC={EnterWOrC} 'Make sure you replace "{EnterWOrC}" with a "w" or a "c" and BE SURE TO PUT QUOTES AROUND THE LETTER.
For Wscript: WorC="W"
For CScript: WorC="C"
It is NOT case Sensitive.

Resources