How to redirect output from EXE to TXT file using VBScript? - windows

The requirement is to execute a certain script on multiple workstations using a tool such as Microsoft SCCM.
This script is required to execute the EXE 'C:\ugs\nx5\UGII\env_print.exe' on every workstation. This is to be done twice using the following parameters :
C:\ugs\nx5\UGII\env_print.exe -m
C:\ugs\nx5\UGII\env_print.exe -n
The script must be designed such that the output from the above mentioned should be stored at someplace on the workstation, from where SCCM could read the values.
To achieve this requirement, I wrote the following VBscript :
--------------------------------------------------------------------------------------------
On Error Resume Next
Const HKEY_LOCAL_MACHINE = &H80000002
Dim WshShell, fso, file, objRegistry, strKeyPath, strSysDrive, outputFile, strTEMP, file2, oTxtFile, oTxtFile2
Dim ComExec, strSysRoot, strComputer, outputFile2, EXEpath, ComExec2, return, return2, text, text2, CMDPath
strComputer = "."
Set WshShell = Wscript.CreateObject("WScript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")
Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
strSysDrive = WshShell.ExpandEnvironmentStrings("%SystemDrive%")
strSysRoot = WshShell.ExpandEnvironmentStrings("%SystemRoot%")
EXEpath = strSysDrive & "\ugs\nx5\UGII\env_print.exe"
CMDPath = strSysRoot & "\system32\cmd.exe"
'-----------------------SET TXT FILE LOCATION-----------------------
outputFile = strSysDrive & "\env_print_m.txt"
outputFile2 = strSysDrive & "\env_print_n.txt"
'-----------------------CREATE TEXT FILES-----------------------
Set oTxtFile = fso.CreateTextFile(outputFile)
Set oTxtFile2 = fso.CreateTextFile(outputFile2)
'-------COMMAND TO EXECUTE AND REDIRECT OUTPUT TO TXT FILE-------
ComExec = CMDPath & " /c " & EXEpath & " -m >> " & outputFile
ComExec2 = CMDPath & " /c " & EXEpath & " -n >> " & outputFile2
'-----------------------EXEUTE COMMANDS-----------------------
return = WshShell.Run(ComExec, 0, true)
return2 = WshShell.Run(ComExec2, 0, true)
'-----------------------READ OUTPUT FROM TXT FILES-----------------------
Set file = fso.OpenTextFile(outputFile, 1)
text = file.ReadAll
file.Close
Set file2 = fso.OpenTextFile(outputFile2, 1)
text2 = file2.ReadAll
file.Close
'-----------------------WRITE OUTPUT VALUES TO REGISTRY STRING VALUES-----------------------
strKeyPath = "SOFTWARE\env_print_Ver"
objRegistry.CreateKey HKEY_LOCAL_MACHINE, strKeyPath
WshShell.RegWrite "HKEY_LOCAL_MACHINE\SOFTWARE\env_print_Ver\env_print_m", text, "REG_SZ"
WshShell.RegWrite "HKEY_LOCAL_MACHINE\SOFTWARE\env_print_Ver\env_print_n", text2, "REG_SZ"
'-----------------------DELETE TXT FILES-----------------------
fso.DeleteFile outputFile
fso.DeleteFile outputFile2
--------------------------------------------------------------------------------------------
This script executes the EXE with the required parameters and stores the output to 2 different TXT files(env_print_m.txt and env_print_n.txt).
Then reads these string values from the text files and stores them as registry string values at the following locations, so that it could be read by SCCM.
HKEY_LOCAL_MACHINE\SOFTWARE\env_print_Ver\env_print_m
HKEY_LOCAL_MACHINE\SOFTWARE\env_print_Ver\env_print_n
However, when this script is executed on workstations running Windows XP, the outputs aren't redirected to the TXT files. No errors are displayed either.
I am at my wits end. Please help.

As your first output file is not named/specified outputFile, change
Set file = fso.OpenTextFile("outputFile", 1)
to
Set file = fso.OpenTextFile(outputFile, 1)
(Same for the second one)
Trying to access a file wrongly named should abort your script with an error message. You switched of this feature by an EVIL global "On Error Resume Next". Get rid of it and see if "outputs aren't redirected' is explained by an error message.
Added wrt comment:
If something like
WshShell.Run ComExec, 0, true
'does not work' you should:
call .Run as a function and check the return value
echo the command (ComExec) and try to execute exactly this command from a console
switch /c to /k and look
sacrify a goat and think about permissions
Oh, I forgot:
manually delete the output files and check if they are created but get no content - then reconder the .exe

Related

VBScript OpenTextFile File Not Found error when Windows Explorer shows the file

I am writing a program in VBScript to automate the process of file encryption, and am struggling with a problem.
I want to test which code the script will execute based on whether a file comparison returns an errorlevel of 0 or 1. (For simplicity, I cut out that code from this post.) Google searches have pointed me to the following to start the process of modifying one of the comparison files for this purpose.
Set testFile = fso.OpenTextFile(testDestFile, 8, False, 0)
However, VBScript always throws a "File not found" error for that line unless I put
WScript.Echo "testDestFile is '" & testDestFile & "'..."
right before it.
I don't want that, because the script's actions should be invisible to the user unless necessary. When I run this script, I can see in Windows Explorer that it creates the file represented by testDestFile. What am I doing wrong?
Option Explicit
Dim baseDirLen, compareOpts, decryptOpts, destDataPath, destFolder, _
destFolderPath, encDestFile, encryptorPath, encryptOpts, file, fileName, _
folder, folderEnd, fso, keyPath, oShell, srcDataPath, srcDirEndLen, _
srcFolder, strErrorCode, testDestFile, testDiff, testFile, t
Set oShell = CreateObject("Wscript.Shell")
Set fso = CreateObject("Scripting.FileSystemObject")
srcDataPath = "e:\EZcrypt\TargetData"
keyPath = "e:\EZcrypt\Key\Demo010719.key.bin"
destDataPath = "E:\EZcrypt\EncryptedData"
encryptorPath = "E:\OpenSSL-Win32\bin\openssl"
Set srcFolder = fso.GetFolder(srcDataPath)
baseDirLen = Len(srcDataPath)
recurseFolders(srcFolder)
Sub recurseFolders(srcFolder)
For Each folder In srcFolder.subfolders
srcDirEndLen = (Len(folder) - baseDirLen - 1)
folderEnd = Right(folder, srcDirEndLen)
destFolderPath = destDataPath & "\" & folderEnd & "\"
If Not fso.FolderExists(destFolderPath) Then
fso.CreateFolder(destFolderPath)
End If
For Each file In folder.Files
fileName = fso.GetFileName(file)
testDestFile = destFolderPath & "test." & fileName
encDestFile = destFolderPath & fileName & ".enc"
If Not fso.FileExists(encDestFile) Then
strErrorCode = ""
encryptOpts = encryptorPath & " enc -aes-256-cbc -salt -in """ & _
file & """ -out """ & encDestFile & _
""" -pass file:""" & keyPath & """ -pbkdf2"
oShell.Run (encryptOpts)
decryptOpts = encryptorPath & " enc -d -aes-256-cbc -in """ & _
encDestFile & """ -out """ & testDestFile & _
""" -pass file:""" & keyPath & """ -pbkdf2"
oShell.Run (decryptOpts)
WEcript.Echo "testDestFile is '" & testDestFile & "'..."
Set testFile = fso.OpenTextFile(testDestFile, 8, False, 0)
Else
WScript.Echo "'" & encDestFile & "' exists. Skipping..."
End If
Next
recurseFolders(folder)
Next
End Sub
The most likely reason for the behavior you observed is that the openssl commands you run right before trying to open that file (specifically the encryption command, which appears to be creating the file) haven't finished yet. You don't tell the Run method to wait for the commands to return, so they're running asynchronously in the background. Presumably the WScript.Echo adds just enough delay for the encryption to finish before the code proceeds to opening the file. Using WScript.Sleep instead of echoing something would probably have had the same effect.
To fix the issue, wait for the external commands to return.
Replace these lines:
encryptOpts = encryptorPath & ...
oShell.Run (encryptOpts)
decryptOpts = encryptorPath & ...
oShell.Run (decryptOpts)
with this:
encryptOpts = encryptorPath & ...
oShell.Run encryptOpts, 0, True
decryptOpts = encryptorPath & ...
oShell.Run decryptOpts, 0, True
It's also good practice to check the exit status of external commands, so you can see if something went wrong:
rc = oShell.Run(encryptOpts, 0, True)
If rc <> 0 Then
'an error occurred
End If

Unable to find file in VBScript

The VBE encoder section works (from prior experience but this time around it cannot find the required file. The file is in the %temp% folder so there are spaces in the path but I have used the "" as can be seen in the code. The MsgBox shows the correct file and I can confirm its existence but the code fails when fso.GetFile.
This is part of a larger script that is called with the target file (full path) as the argument. The target file is previously created by the calling script.
Main script (gets called with target file):
Set fso = CreateObject("Scripting.FileSystemObject")
Set wshShell = WScript.CreateObject("WScript.Shell")
textFile = WScript.Arguments(0)
GetExtension = fso.GetExtensionName(fso.GetFileName(textFile))
If LCase(GetExtension) = "vbs" Then
Set oFilesToEncode = WScript.Arguments
Set oEncoder = CreateObject("Scripting.Encoder")
For i = 0 To oFilesToEncode.Count - 1
file = """" & Trim(oFilesToEncode(i)) & """"
MsgBox file
If fso.FileExists(Left(file, Len(file) - 3) & "vbe") Then fso.DeleteFile(Left(file, Len(file) - 3) & "vbe")
Set oFile = fso.GetFile(file)
Set oStream = oFile.OpenAsTextStream(1)
sSourceFile = oStream.ReadAll
oStream.Close
sDest = oEncoder.EncodeScriptFile(".vbs", sSourceFile, 0, "")
sFileOut = Left(file, Len(file) - 3) & "vbe"
Set oEncFile = fso.CreateTextFile(sFileOut)
oEncFile.Write sDest
oEncFile.Close
Next
End If
WScript.Quit
Section of calling script:
Do While fso.FileExists(strTempVBS) = False
Loop
strKey = "HKEY_CLASSES_ROOT\Engineers-Toolbox\Multi-Tool\Installed\Path\"
value = wshShell.RegRead( strKey )
arg = " " & strTempVBS
running = "C:\Custom\Multi-Tool\Multi-Tool.exe " & """" & arg & """"
wshShell.Run running, True
I have tried using hard coding the path to the exe to get it going, 'value' contains the path to the main script.
Do not prematurely add quotes around paths. I'm always confused why people keep doing this, because it creates more problems than it solves. Add double quotes when they're actually required, but not before.
FileSystemObject methods can handle paths with spaces without the additional double quotes. In fact, they will interpret double quotes in a path string as part of the path and throw an error, because they can't find a file with a double quote in its name (which would be invalid anyway).
Your check for the existence of a file also doesn't work, because you don't account for the quotes you added to the path string:
file = """C:\some\folder\file.vbs"""
WScript.Echo file
WScript.Echo Left(file, Len(file) - 3) & "vbe"
The output of the above code snippet is
"C:\some\folder\file.vbs"
"C:\some\folder\file.vvbe
Change this:
file = """" & Trim(oFilesToEncode(i)) & """"
into this:
file = Trim(oFilesToEncode(i))
and the problem will disappear.
Ok, the solution is to (predictably) add the quotes in the calling script, that way the VBS to VBE encoder section can remain standard.
strTEMP = wshShell.ExpandEnvironmentStrings( "%UserProfile%" ) & "\AppData\Local\Multi-Tool\"
strTempVBS = strTEMP & "observe.vbs"
strKey = "HKEY_CLASSES_ROOT\Engineers-Toolbox\Multi-Tool\Installed\Path\"
value = wshShell.RegRead( strKey ) & " "
running = value & "" & chr(34) & strTempVBS & chr(34) & ""
wshShell.Run running ,True
Interesting was how the quotes are added. This made the called script find the required file even tho the full path contained spaces.

vbs/batch folder name with spacing issue

Im having an issue where i am trying to create shortcuts but the vbs script is cutting out when it reaches a space in the path.
i have had a look around but many of the ones i have seen deal with the string being in vbs not being passed from a batch file.
here is my code so you can get a better understanding
Batch File:
#echo off
set office7="C:\ProgramData\Microsoft\Windows\Start Menu\Strategix Programs\Office Programs"
mkdir %office7%
cscript "H:\Installation Batch Files\createLink.vbs" ""%office7%\Purchase Order Entry.lnk"" "\\192.168.0.7\Temp\stock\Porder10.exe" "T:\Stock"
pause
Vbs file:
Set oShell = CreateObject("WScript.Shell") Set args = WScript.Arguments
sShortcut = oShell.ExpandEnvironmentStrings("" & args.Item(0) & "") sTarget = args.Item(1) sStartIn = args.Item(2)
WScript.Echo "Shortcut: " & sShortcut WScript.Echo "Target: " & sTarget WScript.Echo "StartIn: " & sStartIn
Output:
Shortcut: C:\ProgramData\Microsoft\Windows\Start Menu\Strategix Programs\Office Programs\Purchase
Target: Order
StartIn: Entry.lnk
Batch part
#echo off
set "office7=C:\ProgramData\Microsoft\Windows\Start Menu\Strategix Programs\Office Programs"
mkdir "%office7%"
cscript "H:\Installation Batch Files\createLink.vbs" "%office7%\Purchase Order Entry.lnk" "\\192.168.0.7\Temp\stock\Porder10.exe" "T:\Stock"
pause
The "correct" way of dealing with quotes is not include them in the value. If later you need them, adding them is easy (look the mkdir command and the arguments), but removing them is not. Without a good reason, do not include them. So, the "correct" way is
set "var=value"
That will assign the value to the variable, take care of problematic characters (all the assignation is inside quotes) and keep possible spaces at the end of the line out of the variable value.
Now, to the vbs part
Dim oShell
Set oShell = CreateObject("WScript.Shell")
Dim args
Set args = WScript.Arguments
Dim sShortcut, sTarget, sStartIn
sShortcut = args.Item(0)
sTarget = args.Item(1)
sStartIn = args.Item(2)
WScript.Echo "Shortcut: " & sShortcut
WScript.Echo "Target: " & sTarget
WScript.Echo "StartIn: " & sStartIn
There is no need for ExpandEnvironmentStrings, this has been done when the batch line was parsed in cmd. %office7% is a reference to the value of the variable, not the name of the variable, and the parser replaces variable reads with variable values.
And for the shortcut creation
With oShell.CreateShortcut( sShortcut )
.TargetPath = sTarget
.WorkingDirectory = sStartIn
.Save
End With

VBS Run cmd.exe output to a variable; not text file

This is what I have so far. It works; outputing the folder path to temp to a text file.
What I really want, is to output the data to a variable. Every example I see online, show how to do this using something like:
set objScriptExec = wshShell.Exec (strCommand)
followed by
strresult = LCase(objScriptExec.StdOut.ReadAll. // code
I want this to run with Run, not Exec, because I want the command prompt windows to be hidden as I will performing many commands with the code below. How can I capture that output to a variable?
Set wsShell = CreateObject("WScript.Shell")
strCommand = "cmd /c echo %temp% > %temp%\test.txt"
wsShell.Run strcommand,0,True
This may be done with the Windows Script Host Exec command. StdOut, StdIn, and StdErr may all be accessed, and ERRORLEVEL is available when the command completes.
Dim strMessage, strScript, strStdErr, strStdOut
Dim oExec, oWshShell, intErrorLevel
Dim ComSpec
Set oWshShell = CreateObject("WScript.Shell")
ComSpec = oWshShell.ExpandEnvironmentStrings("%comspec%")
intErrorLevel = 0
strScript = ComSpec & " /C echo %temp%"
On Error Resume Next
Set oExec = oWshShell.Exec (strScript)
If (Err.Number <> 0) Then
strMessage = "Error: " & Err.Message
intErrorLevel = 1
Else
Do While oExec.Status = 0
Do While Not oExec.StdOut.AtEndOfStream
strStdOut = strStdOut & oExec.StdOut.ReadLine & vbCrLf
Loop
Do While Not oExec.StdErr.AtEndOfStream
strStdErr = strStdErr & oExec.StdErr.ReadLine & vbCrLf
Loop
WScript.Sleep 0
Loop
intErrorLevel = oExec.ExitCode
strMessage = strStdOut & strStdErr & CStr(intErrorLevel)
End If
WScript.Echo (strMessage)
NOTE: Replacing "ReadLine" above with "Read(1)" accomplishes the same thing, but adds an ability to process characters rather than whole lines.
Of course Wscript.Shell would be a lot easier, but, since you want more fine grain control of your session, consider using Win32_Process. Usually, one uses this to control the placement of a new window, but, in your case, you want it hidden, so I set startupInfo.ShowWindow = 0 which means SW_HIDE. The following declares a VBScript function called RunCmd and which will run a command in an invisible window saving the output to a text file and then return the contents of the text file to the caller. As an example, I invoke RunCmd with the HOSTNAME command:
Function RunCmd(strCmd)
Dim wmiService
Set wmiService = GetObject("winmgmts:\\.\root\cimv2")
Dim startupInfo
Set startupInfo = wmiService.Get("Win32_ProcessStartup")
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim cwd
cwd = fso.GetAbsolutePathname(".")
startupInfo.SpawnInstance_
startupInfo.ShowWindow = 0
' startupInfo.X = 50
' startupInfo.y = 50
' startupInfo.XSize = 150
' startupInfo.YSize = 50
' startupInfo.Title = "Hello"
' startupInfo.XCountChars = 36
' startupInfo.YCountChars = 1
Dim objNewProcess
Set objNewProcess = wmiService.Get("Win32_Process")
Dim intPID
Dim errRtn
errRtn = objNewProcess.Create("cmd.exe /c """ & strCmd & """ > out.txt", cwd, startupInfo, intPID)
Dim f
Set f = fso.OpenTextFile("out.txt", 1)
RunCmd = f.ReadAll
f.Close
End Function
MsgBox RunCmd("HOSTNAME")
References:
Create method of the Win32_Process class
Win32_ProcessStartup class

VBScript Environment variables

I have a question regarding how I should go about fixing an error that I am seeing when running my script. I am pretty sure it has to do with the way in which I am using the %COMPUTERNAME% environment variable.
What my script does is it zips up some files locally, then copies them using robocopy to a mounted or shared drive, then checks to see if the file sizes are the same, and if they are then it deletes the files on the original computer. If any step in the process produces an error it exits the script.
Now the script works perfectly fine if I do not add in the "%COMPUTERNAME%" to the final destination path. (Where the zipped files will eventually be) I need the zipped files to be placed into their own folders with the name of the host from which it originated, because this script will be run on many different machines all going to the same location.
So basically it needs to look something like this:
E:\LocalHostName\TestZip.zip
Now the script will build the folder just fine when the zipped files are being copied over, the problem occurs once the file size check starts. I am getting the error of "File not found" for the line "FileToBeCompared2". I understand why the error is being produced, because it is not recogizing the %COMPUTERNAME% environment variable, but I do not know how to go about addressing this issue.
I am also going to try to add in some functionality where if an error occurs a text file with something like "An error occured during the script" is produced in the output folder.
Thank you for all your help in advance. The script is found below:
'-------------------------------------------------------------------------------------------
'This script is used to zip files locally, copy them to a new location, verify that the
'files were copied correctly, and then delete the files from the original source.
'In it's current state it is being used as a means to zip event files and move them
'to a central location.
'Run with administrator priveleges.
'-----------------------------------------------------------------------------------------------------
Option Explicit
Dim sDirectoryPath, sLocalDestinationPath, sFinalDestinationPath, sOutputFilename, Shell, sFileExt, sFilePrefix
Set Shell = WScript.CreateObject("WScript.Shell")
'Specify Directory Path where files to be zipped are located
'Specify local destination for zipped files
'Specify final destination path for zippped files
'Specify file extension name to look for
'Specify prefix of filename to look for
sDirectoryPath = "C:\Testscripts\"
sLocalDestinationPath = "C:\ScriptOutput\"
sFinalDestinationPath = "E:\CopyTestFolder\" & sOutputFilename & "\"
sFileExt = ".evtx"
sFilePrefix = "Archive*"
sOutputFilename = shell.ExpandEnvironmentStrings("%COMPUTERNAME%") 'Environment variables needed for grabbing hostname
Dim ZipCommand, RobocopyCommand, RunCommand, filesys, filetext
Dim d : d = Date()
Dim dateStr : dateStr = Year(d) & "-" & Right("00" & Month(d), 2) & "-" & Right("00" & Day(d), 2) 'Date String
Dim t : t = Time()
Dim timeStr: timeStr = Hour(t) & "-" & Right("00" & Minute(t), 2) & "-" & Right("00" & Second(t), 2) 'Time String
Dim FullFileName
FullFileName = sOutputFilename & "-" & dateStr & "-" & timeStr & ".zip "
'Following command runs 7-zip and grabs the files to be zipped from your set sDirectoryPath, zips them into set sLocalDestinationPath
'and names the file with the localhost name and date/time
ZipCommand = """C:\Program Files\7-zip\7z.exe"" a " & sLocalDestinationPath & FullFileName & sDirectoryPath & sFilePrefix & sFileExt
RunCommand = Shell.Run(ZipCommand,0,true)
if err.Number <> 0 then
WScript.Echo "An error has occurred during the zip process, re-run Script." WScript.Quit
end if
Wscript.Sleep 2000
'The following command creates a folder named after the host computer where the files are being copied from
Dim newfolder, newfolderpath, filesys2
newfolderpath = "E:\CopyTestFolder\" & sOutputFilename & "\"
set filesys2 = CreateObject("Scripting.FileSystemObject")
If Not filesys2.FolderExists(newfolderpath) Then
Set newfolder = filesys2.CreateFolder(newfolderpath)
End If
'Following command runs Robocopy from command line, moves files from your set sLocalDestinationPath to your set sFinalDestinationPath
WScript.Echo "Robocopy.exe " & sLocalDestinationPath & " " & sFinalDestinationPath
RobocopyCommand = "Robocopy.exe " & sLocalDestinationPath & " " & sFinalDestinationPath
RunCommand = Shell.Run(RobocopyCommand,0,true)
if err.Number <> 0 then
WScript.Echo "An error has occured copying the files, re-run Script."
WScript.Quit
end if
Dim fso, FileToBeCompared1, FileToBeCompared2
Set fso = CreateObject("Scripting.FileSystemObject")
'Setting the Local file to be compared
Set FileToBeCompared1 = fso.GetFile(sLocalDestinationPath & FullFileName)
WScript.echo sFinalDestinationPath & FullFileName
'Setting the file copied to final destination to be compared
Set FileToBeCompared2 = fso.GetFile(sFinalDestinationPath & FullFileName)
If FileToBeCompared1.size = FileToBeCompared2.size then
fso.DeleteFile("C:\Testscripts\Archive*.evtx") 'This will be the path where events are being Archived to. (Non restricted path)
fso.DeleteFolder("C:\ScriptOutput") 'This deletes the archive folder that 7-zip builds each time this script is run
else
WScript.Echo "File sizes do not match, File was not fully copied, Re run script."
WScript.Quit
end if
Because fso.GetFile() will not automatically expand %COMPUTERNAME%, modify sFinalDestinationPath to use sOutputFilename like this:
sOutputFilename = shell.ExpandEnvironmentStrings("%COMPUTERNAME%")
sFinalDestinationPath = "E:\CopyTestFolder\" & sOutputFilename & "\"

Resources