capture command line output in vbscript - windows

Got a simple script that executes a command to a server - briefly:
//Create shell
set WshShell=CreateObject("WScript.Shell")
WshShell.run "cmd.exe"
//send commands
WshShell.SendKeys "telnet IP_ADDRESS"
WshShell.Sendkeys "dir"
Server offers feedback which I want to capture. I just need to capture the first line into a variable, and then just print that variable out to confirm.
Can you help? Thanks.

Do not use the Windows telnet client for automation purposes. The telnet client that ships with Windows was made for interactive use only.
I'd use plink (from the PuTTY suite) in batch mode for this:
plink.exe -telnet -batch IP_ADDRESS dir
The tool doesn't require installation, so you can simply deploy it alongside your script.
Run it either in a batch file using head/tail, or in a VBScript using the Exec method, so you can read from StdOut:
addr = "62.39.x.x"
port = 24
timeout = 300 'seconds
timedOut = False
cmdline = "echo ""mute near get"" | plink.exe -telnet -batch " & addr & " -P " & port
Set sh = CreateObject("WScript.Shell")
'change working directory to directory containing script and plink executable
Set fso = CreateObject("Scripting.FileSystemObject")
sh.CurrentDirectory = fso.GetParentFolderName(WScript.ScriptFullName)
'wait until command completes or timeout expires
expiration = DateAdd("s", timeout, Now)
Set cmd = sh.Exec("%COMSPEC% /c " & cmdline)
Do While cmd.Status = 0 And Now < expiration
WScript.Sleep 100
Loop
If cmd.Status = 0 Then
cmd.Terminate
timedOut = True
End If
WScript.Echo cmd.StdOut.ReadAll
If cmd.ExitCode <> 0 Then
WScript.Echo "Command terminated with exit code " & cmd.ExitCode & "."
WScript.Echo cmd.StdErr.ReadAll
WScript.Quit 1
ElseIf timedOut Then
WScript.Echo "Command timed out."
WScript.Echo cmd.StdErr.ReadAll
WScript.Quit 2
End If

It might not be the best method, but worked for me:
Windows telnet command can save the output in client side using -f arguments. Therefore, you could use:
WshShell.SendKeys "telnet -f D:\output\telnet.out IP_ADDRESS"
and at the end of your script, simply process the content of telnet.out

Related

VBScript not running from windows service

I have a Windows service which calls a bat file. That bat file calls a PowerShell script, and inside that PowerShell script a VBScript is called.
Windows Service > bat file > powershell file > vbscript
When I manually run the bat file, the VBscript is successfully executed, but if I execute the same bat file from the Windows service, then all scripts are called, but the VBScript skips to run.
Executing a bat file manually successfully executes the VBScript, but not via Windows service
I tried to call The VBScript inside PowerShell in different ways:
& c:\windows\system32\cscript.exe NameOfFile.vbs
start-process
invoke-expression
C:\Windows\System32\cscript.exe NameOfFiles.vbs //B //Nologo $IP_SU $RemoteSessions_Output $user
My VBScript is:
dim ip
dim sessions_dir
dim temp
dim username
dim password
set temp = Wscript.Arguments
ip = temp(0)
sessions_dir = temp(1)
username = temp(2)
password = temp(3)
Sub WaitEnter()
WshShell.AppActivate("telnet " & ip )
WScript.Sleep 2000
WshShell.AppActivate("telnet " & ip)
WshShell.SendKeys "{Enter}"
WshShell.AppActivate("telnet " & ip)
WScript.Sleep 2000
End Sub
set WshShell = WScript.CreateObject("WScript.Shell")
Wscript.Sleep 1000
WshShell.AppActivate("telnet " & ip )
WshShell.Run "telnet " & ip & " -f " & sessions_dir & "\" & ip & "_SU_Status_Output.txt",2
WshShell.AppActivate("telnet " & ip)
WScript.Sleep 1000
WshShell.AppActivate("telnet " & ip)
WshShell.SendKeys username
WaitEnter
WshShell.AppActivate("telnet " & ip)
WshShell.SendKeys password
WaitEnter
WshShell.AppActivate("telnet " & ip)
WshShell.SendKeys "SU_INrOmk=` pl | awk '{{}print {$}3{}}' | head -3 | cut -d '=' -f2`; SU_type=` pl | grep $SU_INrOmk | tail -1 | awk '{{}print {$}12{}}'`"
WaitEnter
WshShell.AppActivate("telnet " & ip)
WshShell.SendKeys "echo $SU_type"
WaitEnter
WshShell.AppActivate("telnet " & ip)
WshShell.SendKeys "exit"
WshShell.AppActivate("telnet " & ip)
WshShell.SendKeys "{Enter}"
and PowerShell script from where it is called is like:
if(Test-Path C:\Windows\System32\cscript.exe){
echo "Cscript found"
$command = "& C:\Windows\System32\cscript.exe NameOfFile.vbs $IP_SU $RemoteSessions_Output $user $DecPwd | out-null"
Invoke-Expression -Command $Command
start-Sleep 10
if($?){
start-sleep 10
$SU_Output_File = $IP_SU + "_SU_Status_Output.txt"
$SU_Remote_FilePath = $RemoteSessions_Output + "\" + $SU_Output_File
}
}
i expect that VBScript is called when Windows service calls the bat file.
I see several things that could be causing issues for you here.
I'm no master of VBS, but my guess here is that you are using Wscript which requires interactivity I think, but you should be using Cscript variant calls instead. My guess is that your script may be bombing due to this. Services can not run in an interactive context since Vista/Windows Server 2008.
You are calling this with Invoke-Expression, which (almost) always returns successful even if the cmdlet failed. In other words, Invoke-Expression will (almost) always set $? to $True even if the command failed. However, you can slip in an evaluation of ; $? at the end of your expression which will end up setting $? as you would expect, which breaks the notion of "always sets $? to $True".
However, you are also using $? incorrectly as well. $? only evaluates the success of cmdlets, not commands. cscript.exe is an executable, and must have its success evaluated with the $LASTEXITCODE automatic variable instead. This will be typically be 0 for success and any other value for non-success. You will have to check the success of this $LASTEXITCODE yourself, as even when the ErrorActionPreference is set to Stop, it won't automatically treat non-zero exit codes as terminating errors.
Outside of the scope of this answer, but worth a mention, I would recommend replacing Invoke-Expression with a straight call to the executable and splat your parameters instead.

Run cmd from vbscript silently

I created the following runas.vbs script:
**************
Option explicit
Dim oShell, k
Const PASSWORD = "Pass123~"
set oShell= Wscript.CreateObject("WScript.Shell")
WScript.Sleep 500
oShell.run("RunAs /noprofile /user:%computername%\postgres " & Chr(34) & "cmd /c\" &
Chr(34) & WScript.Arguments(0) & "\" & Chr(34) & Chr(34))
WScript.Sleep 1000
For k=1 To Len(PASSWORD)
WScript.Sleep 200
oShell.SendKeys Mid(PASSWORD,k,1)
Next
Wscript.Quit
**************
I use this vbscript in a Batch file to run initdb.exe (Postgresql).
Used as:
cscript //Nologo //B runasNSPostgres.vbs ""%LG_PATH%\initdb.exe" --locale=C --encoding=UTF-8 -U %DBADMIN% -D "%DBDATA%""
When this command is executed, another command prompt screen opens up which starts the initdb processing. I do not want the new cmd prompt screen to show up. I want the initdb.exe to run in the background.
If you hide the command prompt window, you won't be able to use SendKeys to send it your password keystrokes.
You can use another method, though. Try using the ECHO command and pipe its output to RunAs.
oShell.Run "echo " & PASSWORD & " | runas /noprofile ...", 0
Use a 0 as the 2nd parameter to prevent the window from appearing.

Waiting till the particular message is displayed on console

Here is my VBS code
Set wshshell = wscript.CreateObject("WScript.Shell")
Wshshell.run "C:\Temp\Executable.exe -c -dir C:\Productdir"
'Wait till "This will install the product on your computer. Press OK, Cancel" appears
WScript.Sleep 10000
WshShell.SendKeys "~"
Is it possible "rather than hard-coded sleep of 10 secs" to add something like this for e.g. if consolemessage="This will install the product on your computer. Press OK, Cancel" then WshShell.SendKeys "~"?
Can WScript.StdOut be used to capture the console messages in the above case? I was not able to do it.
You can read StdOut of a process when you execute the program using the Exec method.
Set wshshell = wscript.CreateObject("WScript.Shell")
Set p = Wshshell.Exec("C:\Temp\Executable.exe -c -dir C:\Productdir")
Do While p.Status = 0
output = ""
Do Until p.StdOut.AtEndOfStream
c = p.StdOut.Read(1)
WScript.StdOut.Write c 'write read characters to the command prompt
output = output & c
If InStr(output, "This will install the product") > 0 Then
'do stuff
Exit Do
End If
Loop
WScript.Sleep 100
Loop

FTP:Upload file to FTP and verify

I am using VBS to
Upload a file to FTP
Verify the upload process
I am using the method which creates a text file, fills it with the appropriate command and then execute it using ftp.exe in windows.
FTPCommand = "%systemroot%\System32\ftp.exe -s:session.txt"
FTPCommand = objshell.ExpandEnvironmentStrings(FTPCommand)
objshell.Run FTPCommand,, vbTrue
fso.DeleteFile "session.txt", vbTrue
Part 1 is done using this code:
Set SessionFile = fso.OpenTextFile("session.txt", 2, vbTrue)
With SessionFile
.WriteLine "open abcd.com"
.WriteLine "username"
.WriteLine "pwd"
.WriteLine "cd /Test/Test1"
.WriteLine "put """ & File.Path & """"
.WriteLine "quit"
.Close
End With
FTPCommand = "%systemroot%\System32\ftp.exe -s:session.txt"
FTPCommand = objshell.ExpandEnvironmentStrings(FTPCommand)
objshell.Run FTPCommand,, vbTrue
fso.DeleteFile "session.txt", vbTrue
And Part 2 is done using this code:
Set SessionFile = fso.OpenTextFile("session.txt", 2, vbTrue)
With SessionFile
.WriteLine "open abcd.com"
.WriteLine "username"
.WriteLine "pwd"
.WriteLine "cd /Test/Test1"
.WriteLine "ls"
.WriteLine "close"
.WriteLine "bye"
.Close
End With
FTPCommand = "%systemroot%\System32\ftp.exe -s:session.txt"
FTPCommand = objshell.ExpandEnvironmentStrings(FTPCommand)
set ObjExec=objshell.exec(FTPCommand)
DO WHILE ObjExec.status=0 : wscript.sleep 50 : LOOP
StrTemp=ObjExec.stdout.readall
IF instr(1,StrTemp,File.Name,1)<>0 THEN
AlertMessage = AlertMessage & vbTab & "STATUS: UPLOAD SUCCESSFUL" & vbCrLf & vbCrLf
ELSE
AlertMessage = AlertMessage & vbTab & "STATUS: UPLOAD FAILED" & vbCrLf & vbCrLf
END IF
fso.DeleteFile "session.txt", vbTrue
The problem is that (in part 2 code) the code after
DO WHILE ObjExec.status=0 : wscript.sleep 50 : LOOP
never returns.So the file gets uploaded but the code to check the status never returns.
The session.txt file does not get delete and when I execute the command
%systemroot%\System32\ftp.exe -s:session.txt
manually it indeed shows me the list of files (because of ls command).
I have 3 questions:
Why it does not return. Where to start debugging from?
Is there anyway I can upload the file and check its status(maybe by
the error code returned by the ftp command after the "put" command).
Is there is an incorrect directory specified in the code to upload
file, the cd command fails and it incorrectly "puts" the file in the
root folder. same goes for the code checking the file upload. So
even if the directory specified in wrong, the program returns it as
successful
Edit 1:
I tried it using
.WriteLine "cd /Test"
and it worked. Is that directory switching (two folders deep)causing the problem ?
Edit 2:
I ran the ls command manually and it ran fine. The output is:
226 Transfer complete.
ftp: 586493 bytes received in 4.28Seconds 137.00Kbytes/sec.
Is 586493 bytes too much for this ?
I believe the problem may be:
1)The large no of files returned by LS command.
2)The directory structure I am accessing.
Edit:3
From this microsoft website it looks like the above point 1 is the culprit:
A console application's StdOut and StdErr streams share the same
internal 4KB buffer. In addition, the WshScriptExec object only
provides synchronous read operations on these streams. Synchronous
read operations introduce a dependency between the calling script
reading from these streams and the child process writing to those
streams, which can result in deadlock conditions.
I believe the problem is the way you read the stdout from the process. I use this technique succesfully as follows, sorry, I have no time to adapt your script and try it out, so that is up to you.
The following executes a ping to check if the server is online.
Function IsOnline(Address)
Dim strText
IsOnline = False
Set oExecObj = oShell.Exec("%comspec% /c ping -a -n 1 -w 20 " & Address)
Do While Not oExecObj.StdOut.AtEndOfStream
strText = oExecObj.StdOut.ReadAll()
If Instr(strText, "Reply from") > 0 Then
IsOnline = True
Exit Function
End If
Loop
End Function

VBscript and CMD writing into a text file

I am writing a script that executes and write everything to the file
here is example,
I stored the complete command in the variable 'Command' ,
Command = "ftp ftp.xyz.com 21 " & vbCRLF
and then executing it in command prompt,
shell.Run "%comspec% /c FTP " & Command & " > " & E:/abc.txt, 0, TRUE
but when this program execute it won't write anything to the text file because this is an incomplete command, this command on execution prompt user to input username and password of FTP,
how can i do this , that my programm automatically input username and password when prompt and then write everything to file ?
You need to run FTP using an unattended script. (Try ftp /? and look at the -s switch.)
It looks like this:
Const HOSTNAME = "ftp.myserver.com"
Const USERNAME = "Login"
Const PASSWORD = "password"
Set WshShell = CreateObject("WScript.Shell")
Set objFso = CreateObject("Scripting.FileSystemObject")
Set objFile = objFso.CreateTextFile("session.txt")
With objFile
.WriteLine "USER username"
.WriteLine "password"
.WriteLine "cd /public_html/" ' continue adding commands like this
.Close
End With
strOutput = "C:\somefilepath\output.txt"
strCommand = "%systemroot%\System32\ftp.exe -s:session.txt > " & strOutput
strCommand = WshShell.ExpandEnvironmentStrings(strCommand)
WshShell.Run strCommand, 0, vbTrue
objFso.DeleteFile "session.txt", vbTrue
You can read more in my article Using FTP in WSH on ASP Free. I also answered a related question here.

Resources