vbs text concatenation not working with wscript.arguments - vbscript

I made a js script to convert text to speech and running a command line with vbs to keep the console window hidden. I am calling via a command so Im trying to use the parameters passed in to it.
I tried using the '+' operator like in most other languages but it didn't work, then I tried the '&' operator but to no success :(.
Set oShell = CreateObject ("Wscript.Shell")
Dim strArgs
strArgs = "cmd /c node audio.js" + WScript.Arguments.Item(0)
oShell.Run strArgs, 0, false

This
C:\Users\eh
type 56861329.vbs
WScript.Echo "cmd /c node audio.js" + WScript.Arguments.Item(0)
C:\Users\eh
cscript 56861329.vbs MissingSpace
cmd /c node audio.jsMissingSpace
should show the real problem. As #Geert said, & is the VBScript operator for concatenation and should be used.

I forgot a space I feel so dumb

Related

stopping VBScript exec from flashing on screen [duplicate]

I'm trying to execute this simple test script, but a command shell window is appearing after I execute the script.:
Set objShell = WScript.CreateObject("WScript.Shell")
strCommand = "cmd /C tasklist"
Set objExecObject = objShell.Exec(strCommand)
wscript.echo "Test"
How can I prevent it from showing up?
Update
I was able to improve it with this code change:
strCommand = "cmd /C /Q tasklist"
Now the window only shows up for a split second. But I don't want it to show up at all.
You're always going to get a window flash with Exec(). You can use Run() instead to execute the command in a hidden window. But you can't directly capture the command's output with Run(). You'd have to redirect the output to a temporary file that your VBScript could then open, read, and delete.
For example:
With CreateObject("WScript.Shell")
' Pass 0 as the second parameter to hide the window...
.Run "cmd /c tasklist.exe > c:\out.txt", 0, True
End With
' Read the output and remove the file when done...
Dim strOutput
With CreateObject("Scripting.FileSystemObject")
strOutput = .OpenTextFile("c:\out.txt").ReadAll()
.DeleteFile "c:\out.txt"
End With
The FileSystemObject class has methods like GetSpecialFolder() to retrieve the path of Windows temp folder and GetTempName() to generate a temporary filename that you can use instead of hardcoding an output filename as I've done above.
Also note that you can use the /FO CSV argument with tasklist.exe to create a CSV file which should make parsing it much easier.
Finally, there are VBScript "native" ways to retrieve the list of running processes. WMI's Win32_Process class, for example, can do this without the need for Run/Exec.
Edit:
For the sake of completeness, I should mention that your script can relaunch itself in a hidden console window where you can run Exec() silently. Unfortunately, this hidden console window will also hide your output from functions like WScript.Echo(). Aside from that, however, you probably won't notice any differences running your script under cscript vs wscript. Here's an example of this method:
' If running under wscript.exe, relaunch under cscript.exe in a hidden window...
If InStr(1, WScript.FullName, "wscript.exe", vbTextCompare) > 0 Then
With CreateObject("WScript.Shell")
WScript.Quit .Run("cscript.exe """ & WScript.ScriptFullName & """", 0, True)
End With
End If
' "Real" start of script. We can run Exec() hidden now...
Dim strOutput
strOutput = CreateObject("WScript.Shell").Exec("tasklist.exe").StdOut.ReadAll()
' Need to use MsgBox() since WScript.Echo() is sent to hidden console window...
MsgBox strOutput
Of course, if your script expects command-line parameters, those would need to be forwarded when relaunching your script as well.
Edit 2:
Yet another possibility is to use the Windows clipboard. You can pipe the output of your command to the clip.exe utility. Then, retrieve the text via any number of available COM objects that can access the contents of the clipboard. For example:
' Using a hidden window, pipe the output of the command to the CLIP.EXE utility...
CreateObject("WScript.Shell").Run "cmd /c tasklist.exe | clip", 0, True
' Now read the clipboard text...
Dim strOutput
strOutput = CreateObject("htmlfile").ParentWindow.ClipboardData.GetData("text")
You can use .Exec() method, without console window flash, temp files and unexpected WScript.Echo output muting. The method is slightly tricky, and requires to launch secondary linked script, so I added the comments:
Option Explicit
Dim objDummy, strSignature, objPrimary, objSecondary, objContainer, objWshShell, objWshShellExec, strResult
' this block is executed only in the secondary script flow, after primary script runs cscript
If WScript.Arguments.Named.Exists("signature") Then
' retrieve signature string from argument
strSignature = WScript.Arguments.Named("signature")
Do
' loop through all explorer windows
For Each objContainer In CreateObject("Shell.Application").Windows
' check if the explorer's property with signature name contains the reference to the live script
If ChkVBScriptTypeInfo(objContainer.getProperty(strSignature)) Then
Exit Do
End If
Next
WScript.Sleep 10
Loop
' create shell object within secondary script
Set objWshShell = CreateObject("WScript.Shell")
' retrieve the primary script me object reference from explorer's property with signature name
Set objPrimary = objContainer.getProperty(strSignature)
' quit explorer window to release memory as it's no longer needed
objContainer.Quit
' assign the secondary script me object to the primary script's variable
Set objPrimary.objSecondary = Me
' emtpy loop while primary script is working
Do While ChkVBScriptTypeInfo(objPrimary)
WScript.Sleep 10
Loop
' terminate secondary
WScript.Quit
End If
' the code below is executed first in the primary script flow
' create signature string
strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
' create new hidden explorer window as container to transfer a reference between script processes
Set objContainer = GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}")
' put this script's me object reference into explorer's property
objContainer.putProperty strSignature, Me
' launch new secondary process of the same script file via cscript.exe with hidden console window, providing signature string in named argument to identify host script
CreateObject("WScript.Shell").Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0
' wait until secondary script has been initialized and put his me object into this script variable
Do Until ChkVBScriptTypeInfo(objSecondary)
WScript.Sleep 10
Loop
' here is your code starts...
' create exec object within hidden console window of secondary script, execute cmd instruction
Set objWshShellExec = objSecondary.objWshShell.Exec("%comspec% /c tasklist")
' read cmd output
strResult = objWshShellExec.StdOut.ReadAll()
WScript.Echo strResult
' ...
' utility check if me object is live
Function ChkVBScriptTypeInfo(objSample)
On Error Resume Next
If TypeName(objSample) <> "VBScriptTypeInfo" Then
ChkVBScriptTypeInfo = False
Exit Function
End If
ChkVBScriptTypeInfo = True
End Function
UPDATE
I've slightly reworked the code to make it more straightforward:
Option Explicit
Dim strCmd, strRes, objWnd, objParent, strSignature
If WScript.Arguments.Named.Exists("signature") Then WshShellExecCmd
strCmd = "%comspec% /c tasklist"
RunCScriptHidden
WScript.Echo strRes
Sub RunCScriptHidden()
strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}").putProperty strSignature, Me
CreateObject("WScript.Shell").Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0, True
End Sub
Sub WshShellExecCmd()
For Each objWnd In CreateObject("Shell.Application").Windows
If IsObject(objWnd.getProperty(WScript.Arguments.Named("signature"))) Then Exit For
Next
Set objParent = objWnd.getProperty(WScript.Arguments.Named("signature"))
objWnd.Quit
objParent.strRes = CreateObject("WScript.Shell").Exec(objParent.strCmd).StdOut.ReadAll()
WScript.Quit
End Sub
BTW, here is VBScript "multithreading" implementation that uses the same container approach.
Some great suggestions are listed above. I'd like to make one more suggestion which is more of a workaround. You can use Sysinternals Desktops (a free program) to run your macro on another desktop on your same machine. That way the flashing can all happen on its own desktop and won't interrupt your work.
I use Sysinternals PSEXEC
https://learn.microsoft.com/sv-se/sysinternals/downloads/psexec
Created a Batch-file (in the same folder as the vbs and exe-file) that runs the script as system user.
I can not Access the user profile and I need to be local Admin but when i run the script without interaction with the desktop it will hide all annoying popups.
Run Script as system without interaction with desktop
"%~dp0PsExec.exe" -s wscript.exe "%~dp0MyScript.vbs"
Run Script as system with interaction with desktop
"%~dp0PsExec.exe" -s -i wscript.exe "%~dp0MyScript.vbs"
To hide the command line windows in VBscipt is use Run in WshShell Object
Then to get the result you can send this result to text file in %temp%
Then read this result with FileSystemObject
Set Sh = CreateObject("WScript.Shell")
tFile=Sh.ExpandEnvironmentStrings("%Temp%")&"\t.txt"
Sh.Run "cmd.exe /C tasklist > """&tFile&""" ",0,False
Wscript.echo CreateObject("Scripting.FileSystemObject").openTextFile(tFile).readAll()
OR
If StrComp(right(WScript.FullName,11),"wscript.exe",1) = 0 Then '' get only wscript.exe from "c:\windows\system32\wscript.exe" to compere with wscript.exe
WScript.Quit CreateObject("WScript.Shell").Run("cscript.exe """ & WScript.ScriptFullName & """", 0, False)
End If
MsgBox CreateObject("WScript.Shell").Exec("cmd.exe /c tasklist /v /fi ""imagename EQ explorer*"" /FO LIST | FIND ""PID:""").StdOut.ReadAll()
An alternative to using to windows scripting host is here: Run a batch program(.bat) through a Visual Basic 6.0
It runs a program and captures its screen output. It works for me in VB6, but not in VBA (hangs at WaitForSingleObject, don't know why).
After trying the main solutions without success, I was able to solve my problem with the following code:
With CreateObject("WScript.Shell")
.Run "cmd /c start /b tasklist.exe > c:\out.txt", 0, True
End With
The real deal was the "/b" as the console help display:
START ["title"] [/D path] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED]
[/LOW | /NORMAL | /HIGH | /REALTIME | /ABOVENORMAL | /BELOWNORMAL]
[/NODE <NUMA node>] [/AFFINITY <hex affinity mask>] [/WAIT] [/B]
[command/program] [parameters]
"title" Title to display in window title bar.
path Starting directory.
B Start application without creating a new window. The
application has ^C handling ignored. Unless the application
enables ^C processing, ^Break is the only way to interrupt
the application.

Echo Code into a File using VBScript

I am writing a VBScript which I want to use to echo the code of another VBScript into an output file.
However, I am unable to write some of the characters to the output file using this method.
If I use the command line method:
cmd.exe /c "#echo "hello"">output.vbs
This works and the string: "hello" is written to the output file.
However, when I do the same using a VBScript, it does not work.
Set objShell = CreateObject("Wscript.Shell")
objShell.Run "%comspec% /c ""#echo ""hello"">output.vbs"
So, is there a way I can echo it into another file retaining the double quotes?
Thanks.
Your quoting is wrong.
Change this:
objShell.Run "%comspec% /c ""#echo ""hello"">output.vbs"
into this:
objShell.Run "%comspec% /c #echo ""hello"">output.vbs"

How to run a cmd file using vbscript? [duplicate]

I need to run a command to copy a file from one location to another through Command Prompt using a vbs file. this is what I have however it keeps throwing an error at me.
'Dim oShell
Set oShell = WScript.CreateObject ("WScript.Shell")
oShell.run "cmd.exe /C copy "S:Claims\Sound.wav" "C:\WINDOWS\Media\Sound.wav"
Set oShell = Nothing'
The error i get is:
'Script: C:\******\command.vbs
Char: 30
Error: Expected end of statement
Code: 80040401
Source: Microsoft VBScript compilation error'
Please help :)
The problem is on this line:
oShell.run "cmd.exe /C copy "S:Claims\Sound.wav" "C:\WINDOWS\Media\Sound.wav"
Your first quote next to "S:Claims" ends the string; you need to escape the quotes around your files with a second quote, like this:
oShell.run "cmd.exe /C copy ""S:\Claims\Sound.wav"" ""C:\WINDOWS\Media\Sound.wav"" "
You also have a typo in S:Claims\Sound.wav, should be S:\Claims\Sound.wav.
I also assume the apostrophe before Dim oShell and after Set oShell = Nothing are typos as well.
Set oShell = CreateObject ("WScript.Shell")
oShell.run "cmd.exe /C copy ""S:Claims\Sound.wav"" ""C:\WINDOWS\Media\Sound.wav"" "

Hide command prompt window when using Exec()

I'm trying to execute this simple test script, but a command shell window is appearing after I execute the script.:
Set objShell = WScript.CreateObject("WScript.Shell")
strCommand = "cmd /C tasklist"
Set objExecObject = objShell.Exec(strCommand)
wscript.echo "Test"
How can I prevent it from showing up?
Update
I was able to improve it with this code change:
strCommand = "cmd /C /Q tasklist"
Now the window only shows up for a split second. But I don't want it to show up at all.
You're always going to get a window flash with Exec(). You can use Run() instead to execute the command in a hidden window. But you can't directly capture the command's output with Run(). You'd have to redirect the output to a temporary file that your VBScript could then open, read, and delete.
For example:
With CreateObject("WScript.Shell")
' Pass 0 as the second parameter to hide the window...
.Run "cmd /c tasklist.exe > c:\out.txt", 0, True
End With
' Read the output and remove the file when done...
Dim strOutput
With CreateObject("Scripting.FileSystemObject")
strOutput = .OpenTextFile("c:\out.txt").ReadAll()
.DeleteFile "c:\out.txt"
End With
The FileSystemObject class has methods like GetSpecialFolder() to retrieve the path of Windows temp folder and GetTempName() to generate a temporary filename that you can use instead of hardcoding an output filename as I've done above.
Also note that you can use the /FO CSV argument with tasklist.exe to create a CSV file which should make parsing it much easier.
Finally, there are VBScript "native" ways to retrieve the list of running processes. WMI's Win32_Process class, for example, can do this without the need for Run/Exec.
Edit:
For the sake of completeness, I should mention that your script can relaunch itself in a hidden console window where you can run Exec() silently. Unfortunately, this hidden console window will also hide your output from functions like WScript.Echo(). Aside from that, however, you probably won't notice any differences running your script under cscript vs wscript. Here's an example of this method:
' If running under wscript.exe, relaunch under cscript.exe in a hidden window...
If InStr(1, WScript.FullName, "wscript.exe", vbTextCompare) > 0 Then
With CreateObject("WScript.Shell")
WScript.Quit .Run("cscript.exe """ & WScript.ScriptFullName & """", 0, True)
End With
End If
' "Real" start of script. We can run Exec() hidden now...
Dim strOutput
strOutput = CreateObject("WScript.Shell").Exec("tasklist.exe").StdOut.ReadAll()
' Need to use MsgBox() since WScript.Echo() is sent to hidden console window...
MsgBox strOutput
Of course, if your script expects command-line parameters, those would need to be forwarded when relaunching your script as well.
Edit 2:
Yet another possibility is to use the Windows clipboard. You can pipe the output of your command to the clip.exe utility. Then, retrieve the text via any number of available COM objects that can access the contents of the clipboard. For example:
' Using a hidden window, pipe the output of the command to the CLIP.EXE utility...
CreateObject("WScript.Shell").Run "cmd /c tasklist.exe | clip", 0, True
' Now read the clipboard text...
Dim strOutput
strOutput = CreateObject("htmlfile").ParentWindow.ClipboardData.GetData("text")
You can use .Exec() method, without console window flash, temp files and unexpected WScript.Echo output muting. The method is slightly tricky, and requires to launch secondary linked script, so I added the comments:
Option Explicit
Dim objDummy, strSignature, objPrimary, objSecondary, objContainer, objWshShell, objWshShellExec, strResult
' this block is executed only in the secondary script flow, after primary script runs cscript
If WScript.Arguments.Named.Exists("signature") Then
' retrieve signature string from argument
strSignature = WScript.Arguments.Named("signature")
Do
' loop through all explorer windows
For Each objContainer In CreateObject("Shell.Application").Windows
' check if the explorer's property with signature name contains the reference to the live script
If ChkVBScriptTypeInfo(objContainer.getProperty(strSignature)) Then
Exit Do
End If
Next
WScript.Sleep 10
Loop
' create shell object within secondary script
Set objWshShell = CreateObject("WScript.Shell")
' retrieve the primary script me object reference from explorer's property with signature name
Set objPrimary = objContainer.getProperty(strSignature)
' quit explorer window to release memory as it's no longer needed
objContainer.Quit
' assign the secondary script me object to the primary script's variable
Set objPrimary.objSecondary = Me
' emtpy loop while primary script is working
Do While ChkVBScriptTypeInfo(objPrimary)
WScript.Sleep 10
Loop
' terminate secondary
WScript.Quit
End If
' the code below is executed first in the primary script flow
' create signature string
strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
' create new hidden explorer window as container to transfer a reference between script processes
Set objContainer = GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}")
' put this script's me object reference into explorer's property
objContainer.putProperty strSignature, Me
' launch new secondary process of the same script file via cscript.exe with hidden console window, providing signature string in named argument to identify host script
CreateObject("WScript.Shell").Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0
' wait until secondary script has been initialized and put his me object into this script variable
Do Until ChkVBScriptTypeInfo(objSecondary)
WScript.Sleep 10
Loop
' here is your code starts...
' create exec object within hidden console window of secondary script, execute cmd instruction
Set objWshShellExec = objSecondary.objWshShell.Exec("%comspec% /c tasklist")
' read cmd output
strResult = objWshShellExec.StdOut.ReadAll()
WScript.Echo strResult
' ...
' utility check if me object is live
Function ChkVBScriptTypeInfo(objSample)
On Error Resume Next
If TypeName(objSample) <> "VBScriptTypeInfo" Then
ChkVBScriptTypeInfo = False
Exit Function
End If
ChkVBScriptTypeInfo = True
End Function
UPDATE
I've slightly reworked the code to make it more straightforward:
Option Explicit
Dim strCmd, strRes, objWnd, objParent, strSignature
If WScript.Arguments.Named.Exists("signature") Then WshShellExecCmd
strCmd = "%comspec% /c tasklist"
RunCScriptHidden
WScript.Echo strRes
Sub RunCScriptHidden()
strSignature = Left(CreateObject("Scriptlet.TypeLib").Guid, 38)
GetObject("new:{C08AFD90-F2A1-11D1-8455-00A0C91F3880}").putProperty strSignature, Me
CreateObject("WScript.Shell").Run ("""" & Replace(LCase(WScript.FullName), "wscript", "cscript") & """ //nologo """ & WScript.ScriptFullName & """ ""/signature:" & strSignature & """"), 0, True
End Sub
Sub WshShellExecCmd()
For Each objWnd In CreateObject("Shell.Application").Windows
If IsObject(objWnd.getProperty(WScript.Arguments.Named("signature"))) Then Exit For
Next
Set objParent = objWnd.getProperty(WScript.Arguments.Named("signature"))
objWnd.Quit
objParent.strRes = CreateObject("WScript.Shell").Exec(objParent.strCmd).StdOut.ReadAll()
WScript.Quit
End Sub
BTW, here is VBScript "multithreading" implementation that uses the same container approach.
Some great suggestions are listed above. I'd like to make one more suggestion which is more of a workaround. You can use Sysinternals Desktops (a free program) to run your macro on another desktop on your same machine. That way the flashing can all happen on its own desktop and won't interrupt your work.
I use Sysinternals PSEXEC
https://learn.microsoft.com/sv-se/sysinternals/downloads/psexec
Created a Batch-file (in the same folder as the vbs and exe-file) that runs the script as system user.
I can not Access the user profile and I need to be local Admin but when i run the script without interaction with the desktop it will hide all annoying popups.
Run Script as system without interaction with desktop
"%~dp0PsExec.exe" -s wscript.exe "%~dp0MyScript.vbs"
Run Script as system with interaction with desktop
"%~dp0PsExec.exe" -s -i wscript.exe "%~dp0MyScript.vbs"
To hide the command line windows in VBscipt is use Run in WshShell Object
Then to get the result you can send this result to text file in %temp%
Then read this result with FileSystemObject
Set Sh = CreateObject("WScript.Shell")
tFile=Sh.ExpandEnvironmentStrings("%Temp%")&"\t.txt"
Sh.Run "cmd.exe /C tasklist > """&tFile&""" ",0,False
Wscript.echo CreateObject("Scripting.FileSystemObject").openTextFile(tFile).readAll()
OR
If StrComp(right(WScript.FullName,11),"wscript.exe",1) = 0 Then '' get only wscript.exe from "c:\windows\system32\wscript.exe" to compere with wscript.exe
WScript.Quit CreateObject("WScript.Shell").Run("cscript.exe """ & WScript.ScriptFullName & """", 0, False)
End If
MsgBox CreateObject("WScript.Shell").Exec("cmd.exe /c tasklist /v /fi ""imagename EQ explorer*"" /FO LIST | FIND ""PID:""").StdOut.ReadAll()
An alternative to using to windows scripting host is here: Run a batch program(.bat) through a Visual Basic 6.0
It runs a program and captures its screen output. It works for me in VB6, but not in VBA (hangs at WaitForSingleObject, don't know why).
After trying the main solutions without success, I was able to solve my problem with the following code:
With CreateObject("WScript.Shell")
.Run "cmd /c start /b tasklist.exe > c:\out.txt", 0, True
End With
The real deal was the "/b" as the console help display:
START ["title"] [/D path] [/I] [/MIN] [/MAX] [/SEPARATE | /SHARED]
[/LOW | /NORMAL | /HIGH | /REALTIME | /ABOVENORMAL | /BELOWNORMAL]
[/NODE <NUMA node>] [/AFFINITY <hex affinity mask>] [/WAIT] [/B]
[command/program] [parameters]
"title" Title to display in window title bar.
path Starting directory.
B Start application without creating a new window. The
application has ^C handling ignored. Unless the application
enables ^C processing, ^Break is the only way to interrupt
the application.

Run external command silently

I am trying to run cmd code from vbscript (vbs file) silently.
I have tried this, but it doesnt hide the cmd window.
Dim oShell
Set oShell = WScript.CreateObject ("WScript.Shell")
oShell.run "cmd.exe /K ping example.org"
Set oShell = Nothing
What is the correct way to do that ?
Dim oShell
Set oShell = WScript.CreateObject ("WScript.Shell")
oShell.run "cmd.exe ping example.org",0,True
Set oShell = Nothing
As per the MSDN for .Run you can use the optional parameter for intWindowStyle, which will hide most windows from the screen, by setting it to 0. The True is to tell the operation to wait until completion before completing the script. That is of course optional.
If you hide the window you need to remove /K or else the script will never complete.

Resources