I'm an absolute beginner with VB hence I might ask some silly questions.
I have a VB script getting triggered via a Batch file which results in data being imported for last day.
Below is the code for VB and Batch file.
Please let me know if you see any error in the code.
VB Script
rem
rem XLink_Import.vbs
rem
Set oShell = WScript.CreateObject("WScript.Shell")
' filename = oShell.ExpandEnvironmentStrings("today_xlink.bat")
' Set objFileSystem = CreateObject("Scripting.fileSystemObject")
' Set oFile = objFileSystem.CreateTextFile(filename, TRUE)
Dim i
Dim ImportStartOffset, ImportedNumberOfDays
If WScript.Arguments.length > 0 Then
For i=0 to WScript.Arguments.length-1
Arg = WScript.Arguments(i)
If Left(Arg,1) = "-" Then
If ( Arg = "-o" ) Then
ImportStartOffset = WScript.Arguments(i+1)
End if
If ( Arg = "-n" or Arg = "-l" ) Then
ImportedNumberOfDays = WScript.Arguments(i+1)
End if
End if
Next
End If
rem Prepare the import start date
Dim Dy, Mth
Dim ImportDate
ImportDate = Now + ImportStartOffset
Dy = Day(ImportDate)
Mth = Month(ImportDate)
If Len(Dy) = 1 Then Dy = "0" & Dy
If Len(Mth) = 1 Then Mth = "0" & Mth
ImportStartDate = Dy & "/" & Mth & "/" & Year(ImportDate)
rem Prepare import script to run (not useed yet)
rem oFile.WriteLine("isps_ul.exe -t -d " & todaydate & " -L 1")
rem oFile.Close
rem Run XLink import
wscript.echo "isps_ul.exe -t -d " & ImportStartDate & " -L " & ImportedNumberOfDays
oShell.Run "isps_ul.exe -t -d " & ImportStartDate & " -L " & ImportedNumberOfDays, 1, true
Batch File
#echo off
rem
rem XLink_Import.bat
rem
rem Manually starts an Xlink import starting today + a StartOffset of some days.
rem Imported number of days can also be set.
rem
set ImportStartOffset=0
set ImportedNumberOfDays=1
cscript XLink_Import.vbs -o %ImportStartOffset% -n %ImportedNumberOfDays%
pause
You don't need both a batch and a script, one of either would be enough, doing the whole thing in batch would require with some suggling with special parameters and I'm not into that so I'll adapt your script a bit like below.
Since you keep your 2 confiuration variables in the windows environment you can read them from vbscript as well, other option would be to read from a configuration file, from the command line like you did or keep the in the script itself.
Your middle part, makig sure the date is speleld correct could be omitted if you set those dates correctly in the configuration (environmentvariables).
If your import is going to work you should check before by running what is displayed as command, so eg "eisps_ul.exe -t -d28/11/2016 -L" should run, otherwise search on that problem first.
What I meant in my comment about being DRY means you should not repeat things, in case of your command you can store the concatenated command in a variable and use that for viewing and running.
Dim ImportStartOffset, ImportedNumberOfDays, oShell, command, Dy, Mth, ImportDate, ImportStartDate
Constant WaitOnReturn = true, WindowStyle = 1 '1 = Activate and display
'read configuration environment variables
Set oShell = CreateObject( "WScript.Shell" )
ImportStartOffset = wshShell.ExpandEnvironmentStrings( "%ImportStartOffset%" )
ImportedNumberOfDays = wshShell.ExpandEnvironmentStrings( "%ImportedNumberOfDays%" )
'Prepare the import start date (not necessary if environmentvariables would be configured well)
ImportDate = Now + ImportStartOffset
Dy = Day(ImportDate)
Mth = Month(ImportDate)
If Len(Dy) = 1 Then Dy = "0" & Dy
If Len(Mth) = 1 Then Mth = "0" & Mth
ImportStartDate = Dy & "/" & Mth & "/" & Year(ImportDate)
'Run XLink import
command = "isps_ul.exe -t -d " & ImportStartDate & " -L " & ImportedNumberOfDays
wscript.echo command
oShell.Run command, WindowStyle, WaitOnReturn
Set oShell = Nothing
Related
I am trying to get the last modified date of a log file on a remote Windows server 2008/2012 with a batch script.
I am connecting to the machine using "net use", and able to see if the file exists.
net use \\X.X.X.X /user:%USERNAME% %PASSWORD%
if exist "\\X.X.X.X\\C$\\Temp\\LogFiles\\abcd" (
echo ABCD file exists on the server
) else (
echo ABCD file does NOT exist on the server
)
Also, I am able to get the last updated time of a local file using forfiles:
for /f "delims=" %%i in ('"forfiles /m MyLocalAbcd /c "cmd /c echo #file was last modified at #ftime" "') do set modif_time=%%i
echo %modif_time%
However, I am not able to get the modified time of the remote file. I tried to provide the complete path - forfiles /M "\X.X.X.X\C$\Temp\LogFiles\abcd" - or even providing the path to the option P of forfiles, but it is not finding the file.
Is there an easy way to get the modified date/time of the remote file?
Also, I am wondering if there is a way to tail the last n lines of the same file with a Windows built-in command.
Any help is appreciated!
Thanks!
Try a dir.
Or use a for loop differently.
for %%A in (\\server\C$\Temp\LogFiles\abcd) do echo %%~tA
A working example if pasted into a command prompt.
for %A in (\\127.0.0.1\C$\windows\win.ini) do echo %~tA
To the other question you sneeked in
more +50 skips first 50 lines.
This VBScript does what you want.
Set Arg = WScript.Arguments
Set WshShell = createObject("Wscript.Shell")
Set Inp = WScript.Stdin
Set Outp = Wscript.Stdout
Set rs = CreateObject("ADODB.Recordset")
With rs
.Fields.Append "LineNumber", 4
.Fields.Append "Txt", 201, 5000
.Open
LineCount = 0
Do Until Inp.AtEndOfStream
LineCount = LineCount + 1
.AddNew
.Fields("LineNumber").value = LineCount
.Fields("Txt").value = Inp.readline
.UpDate
Loop
.Sort = "LineNumber ASC"
If LCase(Arg(1)) = "t" then
If LCase(Arg(2)) = "i" then
.filter = "LineNumber < " & LCase(Arg(3)) + 1
ElseIf LCase(Arg(2)) = "x" then
.filter = "LineNumber > " & LCase(Arg(3))
End If
ElseIf LCase(Arg(1)) = "b" then
If LCase(Arg(2)) = "i" then
.filter = "LineNumber > " & LineCount - LCase(Arg(3))
ElseIf LCase(Arg(2)) = "x" then
.filter = "LineNumber < " & LineCount - LCase(Arg(3)) + 1
End If
End If
Do While not .EOF
Outp.writeline .Fields("Txt").Value
.MoveNext
Loop
End With
To use
Filter reads and writes standard in and standard out only. These are only available in a command prompt.
filter <inputfile >outputfile
filter <inputfile | other_command
other_command | filter >outputfile
other_command | filter | other_command
Cut
filter cut {t|b} {i|x} NumOfLines
Cuts the number of lines from the top or bottom of file.
t - top of the file
b - bottom of the file
i - include n lines
x - exclude n lines
Example
filter cut t i 5 < "%systemroot%\win.ini"
I have the followaing run.vbs script
Rexe = "R-Portable\App\R-Portable\bin\Rscript.exe"
Ropts = "--no-save --no-environ --no-init-file --no-restore --no-Rconsole "
RScriptFile = "runShinyApp.R"
Outfile = "ShinyApp.log"
startChrome = "GoogleChromePortable\App\Chrome-bin\chrome.exe --app=http://127.0.0.1:9999"
strCommand = Rexe & " " & Ropts & " " & RScriptFile & " 1> " & Outfile & " 2>&1"
intWindowStyle = 0 ' Hide the window and activate another window.'
bWaitOnReturn = False ' continue running script after launching R '
' the following is a Sub call, so no parentheses around arguments'
CreateObject("Wscript.Shell").Run strCommand, intWindowStyle, bWaitOnReturn
WScript.Sleep 1000
CreateObject("Wscript.Shell").Run startChrome, intWindowStyle, bWaitOnReturn
It works pretty well in most cases except when the user puts the run.vbs script in a folder with spaces in its name: e.g. if run.vbs is in folder "foo bar", the user gets the error : "C:\Users\[user name]\Desktop\foo" not recognized as internal command...
I don't understand why Rscript.exe looks for the absolute path before running even if it's called from its parent directory using relative path.
I heard about the double quote solution using the absolute path but it doesn't seem to work with .exe scripts (it does though with .bat and .cmd)
Thanks for any help!
Below code will help you
Dim oShell As Object
Set oShell = CreateObject("WScript.Shell")
'run command'
Dim oExec As Object
Dim oOutput As Object
Set oExec = oShell.Exec("C:\Program Files\R\R-3.2.3\bin\Rscript.exe C:\subfolder\YourScript.R " & """" & var1 & """")
Set oOutput = oExec.StdOut
handle the results as they are written to and read from the StdOut object
Dim s As String
Dim sLine As String
While Not oOutput.AtEndOfStream
sLine = oOutput.ReadLine
If sLine <> "" Then s = s & sLine & vbCrLf
Wend
I am new to Windows Scripting. I have a simple script for archiving using WinRAR CLI utility. I have to schedule this script using batch file. During archiving there are some errors and I want them to write in a simple text file or at least I can write entire output of archiving in a file. How can I change my code to do this?
Dim MyDate
Dim OutputFile
const WaitUntilFinished = true, DontWaitUntilFinished = false, ShowWindow = 1, DontShowWindow = 0
MyDate = Replace(Date, "/", "-")
OutputFile = "backup-" & mydate & ".rar"
Set objShell = WScript.CreateObject("WScript.Shell")
objShell.CurrentDirectory = "C:\Users\ABC\Desktop\"
objShell.Run "C:\windows\Rar.exe a .\VBScripts\backups\" & OutputFile & " software", ShowWindow, WaitUntilFinished
objShell.Popup "Archiving Completed Successfully!",5, "Scheduled Backup"
Set objShell = Nothing
Batch file is like this;
#echo off
start /wait C:\Users\ABC\Desktop\VBScripts\scheduled_backup.vbs
Change your command line to include redirection to a log file:
logfile = "C:\path\to\your.log"
objShell.Run "%COMSPEC% /c C:\windows\Rar.exe a .\VBScripts\backups\" & _
OutputFile & " software >""" & logfile & """", ShowWindow, WaitUntilFinished
Use this function instead of WScript.Shell.Run:
' Runs an external program and pipes it's output to
' the StdOut and StdErr streams of the current script.
' Returns the exit code of the external program.
Function Run (ByVal cmd)
Dim sh: Set sh = CreateObject("WScript.Shell")
Dim wsx: Set wsx = Sh.Exec(cmd)
If wsx.ProcessID = 0 And wsx.Status = 1 Then
' (The Win98 version of VBScript does not detect WshShell.Exec errors)
Err.Raise vbObjectError,,"WshShell.Exec failed."
End If
Do
Dim Status: Status = wsx.Status
WScript.StdOut.Write wsx.StdOut.ReadAll()
WScript.StdErr.Write wsx.StdErr.ReadAll()
If Status <> 0 Then Exit Do
WScript.Sleep 10
Loop
Run = wsx.ExitCode
End Function
Call script instead of start in your batch and use redirection:
script //nologo C:\Users\ABC\Desktop\VBScripts\scheduled_backup.vbs 2> errors.txt
I have jumbled up a vbs script to compress files older than 7 days using 7za's command line utility. While most of the logic works fine, I am able to compress single file into single zip file.
The problem arises when I try to add all matching files to one zip file. Below is the code snippet:
strCommand = "7za.exe -mx=9 a " & ObjectFolder & sysDate & ".zip " & strFileName
strRun = objShell.Run(strCommand, 0, True)
Now as per the 2nd line, setting True would make sure the script will wait till command is finished executing. But the problem is 7za is exiting immediately and going to the next loop, processing the next file and since it tries to create same zip file, I get access denied error.
Can someone please help me to fix this?
I have also tested the scenario in command prompt. What I did was, execute below 2 commands simultaneously in separate prompts:
Prompt 1:
C:\7za.exe -mx=9 a test.zip c:\sample1.pdf
Prompt 2:
C:\7za.exe -mx=9 a test.zip c:\sample2.pdf
Prompt 2 resulted in following error:
Error: test.zip is not supported archive
System error:
The process cannot access the file because it is being used by another process.
This is the same error I am getting in my script and I need help in resolving this. Any pointers will be helpful!
UPDATE:
With the great pointers provided by both John and Ansgar, I was able to resolve this! It turned out to be a bug in my script! In my script, I included a check to see if the file is in use by any other process before processing it for archive. So I was checking this by opening the file for appending using:
Set f = objFSO.OpenTextFile(strFile, ForAppending, True)
But before proceeding to process the same file, I was not CLOSING it in the script, hence the error: The process cannot access the file because it is being used by another process
After I closed the file, all went well!
Thanks Again for all the great support I got here!
As a token of gratitude, I am sharing the whole script for anyone's use. Please note that I am not the original author of this, I gathered it from various sources and tweaked it a little bit to suit my needs.
Archive.vbs
Const ForAppending = 8 ' Constant for file lock check
Dim objFSO, objFolder, objFiles, objShell
Dim file, fileExt, fileName, strCommand, strRun, strFile
Dim SFolder, OFolder, Extension, DaysOld, sDate
'''' SET THESE VARIABLES! ''''
SFolder = "C:\SourceFolder\" 'Folder to look in
OFolder = "C:\OutputFolder\" 'Folder to put archives in
Extension = "pdf" 'Extension of files you want to zip
DaysOld = 1 'Zip files older than this many days
''''''''''''''''''''''''''''''
sDate = DatePart("yyyy",Date) & "-" & Right("0" & DatePart("m",Date), 2) & "-" & Right("0" & DatePart("d",Date), 2)
'Create object for playing with files
Set objFSO = CreateObject("Scripting.FileSystemObject")
'Create shell object for running commands
Set objShell = wscript.createObject("wscript.shell")
'Set folder to look in
Set objFolder = objFSO.GetFolder(SFolder)
'Get files in folder
Set objFiles = objFolder.Files
'Loop through the files
For Each file in objFiles
fileName = Split(file.Name, ".")
fileExt = fileName(UBound(fileName))
'See if it is the type of file we are looking for
If fileExt = Extension Then
'See if the file is older than the days chosen above
If DateDiff("d", file.DateLastModified, Now()) >= DaysOld Then
strFile = file.Path
'See if the file is available or in use
Set f = objFSO.OpenTextFile(strFile, ForAppending, True)
If Err.Number = 70 Then ' i.e. if file is locked
Else
f.close
strFName = objFSO.GetBaseName(file.name)
strCommand = "C:\7za.exe -mx=9 a " & OFolder & sDate & ".zip " & strFile
strRun = objShell.Run(strCommand, 0, True)
'wscript.echo strCommand ' un-comment this to check the file(s) being processed
'file.Delete ' un-comment this to delete the files after compressing.
End If
End If
End If
Next
'Cleanup
Set objFiles = Nothing
Set objFolder = Nothing
Set objFSO = Nothing
Set objShell = Nothing
wscript.Quit
===========================
Thanks
-Noman A.
Not quite what you asked for, but here's a batch script I use for a similar task in case that helps get you past of your immediate issue:
ArchiveScriptLog.Bat
::ensure we're in the right directory, then run the script & log the output
cls
pushd "c:\backup scripts"
ArchiveScript.bat > ArchiveScript.log
popd
ArchiveScript.bat
::Paths (must include the \ on the end). There must be no space between the equals and the value
::UNC paths are acceptable
Set FolderToBackup=F:\EnterpriseArchitect\Energy\
Set BackupPath=F:\EnterpriseArchitect\!ARCHIVE\
Set RemoteBackupPath=\\ukccojdep01wok\h$\Energy\cciobis01edc\
Set SevenZip=C:\Program Files (x86)\7-Zip\
::Get DATE in yyyymmdd format; done in two lines to make it easy to change the date format
FOR /F "TOKENS=2,3,4 DELIMS=/ " %%A IN ('echo %Date%') DO (SET mm=%%A&SET dd=%%B&SET yyyy=%%C)
SET strDate=%yyyy%%mm%%dd%
::Set the Backup File to be the backup path with the current date & .zip on the end
Set BackupFile=%BackupPath%%strDate%.zip
::create a zip containing the contents of folderToBackup
pushd %SevenZip%
7z a "%BackupFile%" "%FolderToBackup%"
popd
::go to the archive directory & copy all files in there to the remote location (this accounts for previous errors if the network were unavailable)
pushd "%BackupPath%"
move *.zip "%RemoteBackupPath%"
popd
::delete off backups in the remote location which are older than 90 days
pushd "%RemoteBackupPath%"
forfiles /D -90 /M *.zip /C "cmd /c del #file"
popd
Your command shouldn't return before 7za has finished its task (and it doesn't in my tests). Try changing your code to the following, so you can see what's going on:
strCommand = "7za.exe -mx=9 a " & ObjectFolder & sysDate & ".zip " & strFileName
strCommand = "%COMSPEC% /k " & strCommand
strRun = objShell.Run(strCommand, 1, True)
It may also be a good idea to quote the filenames:
Function qq(str)
qq = Chr(34) & str & Chr(34)
End Function
strCommand = "7za.exe -mx=9 a " & qq(ObjectFolder & sysDate & ".zip") & " " _
& qq(strFileName)
I am so confused with this, I am trying to upload data to FTP through VBS script and it works fine for a single file file but doesn't upload multiple files when I add the script inside a loop.
Also , why do we need to generate a temporary file when remote uploading to FTP,
I mean to say this,
this is the script I am using,
Dim fso, folder, files, strPath
Set fso = CreateObject("Scripting.FileSystemObject")
strPath = "E:/Test"
Set folder = fso.GetFolder(strPath)
Set files = folder.Files
Const hostname = "ftp.domain.com"
Const port = 21
Const username = "username"
Const password = "password"
Const remoteDir = "/"
Const useDefaultsExclusively = True
Const skipConfirmation = True
For each item In files
If InStr(1, item.Name, "txt") <> 0 Then
defaultFile = item.Name
localFile = fso.getFileName(defaultFile)
localDir = fso.getParentFolderName(defaultFile)
Set shell = CreateObject("WScript.Shell")
tempDir = shell.ExpandEnvironmentStrings("%TEMP%")
' temporary script file supplied to Windows FTP client
scriptFile = tempDir & "\" & fso.GetTempName
' temporary file to store standard output from Windows FTP client
outputFile = tempDir & "\" & fso.GetTempName
'input script
script = script & "lcd " & """" & localDir & """" & vbCRLF
script = script & "open " & hostname & " " & port & vbCRLF
script = script & "user " & username & vbCRLF
script = script & password & vbCRLF
script = script & "cd " & """" & remoteDir & """" & vbCRLF
script = script & "binary" & vbCRLF
script = script & "prompt n" & vbCRLF
script = script & "put " & """" & localFile & """" & vbCRLF
script = script & "quit" & vbCRLF
Set textFile = fso.CreateTextFile(scriptFile, True)
textFile.WriteLine(script)
' bWaitOnReturn set to TRUE - indicating script should wait for the program
' to finish executing before continuing to the next statement
shell.Run "%comspec% /c FTP -n -s:" & scriptFile & " > " & outputFile, 0, TRUE
Wscript.Sleep 10
' open standard output temp file read only, failing if not present
Set textFile = fso.OpenTextFile(outputFile, 1, 0, -2)
results = textFile.ReadAll
textFile.Close
End If
Next
fso.DeleteFile(scriptFile)
fso.DeleteFile(outputFile)
Why do we creating a Temporary file with this code, :S
Set shell = CreateObject("WScript.Shell")
tempDir = shell.ExpandEnvironmentStrings("%TEMP%")
' temporary script file supplied to Windows FTP client
scriptFile = tempDir & "\" & fso.GetTempName
' temporary file to store standard output from Windows FTP client
outputFile = tempDir & "\" & fso.GetTempName
Set textFile = fso.CreateTextFile(scriptFile, True)
textFile.WriteLine(script)
' bWaitOnReturn set to TRUE - indicating script should wait for the program
' to finish executing before continuing to the next statement
shell.Run "%comspec% /c FTP -n -s:" & scriptFile & " > " & outputFile, 0, TRUE
Wscript.Sleep 10
' open standard output temp file read only, failing if not present
Set textFile = fso.OpenTextFile(outputFile, 1, 0, -2)
results = textFile.ReadAll
textFile.Close
Although I am looping through the script , it must upload every txt file inside current directory but it uploads only the first one, why is it so ?
When I print the current file i.e
MsgBox defaultfile
it display the name of each txt file inside the current folder but uploads first file only.
There is no native method of live automation for FTP. This is actually a scripting workaround. Your script is creating a text file with FTP instructions. Then, it launches FTP from the command line using the -s switch. That -s switch allows you to provide a text file containing session commands for FTP.exe to execute before closing. So while your script isn't actually automating the FTP program directly, it simulates automation by running FTP in "unattended mode". You can get a better understanding by opening a command prompt and typing ftp /?.
[EDIT]
I apologize, I missed your second question. Your script is only uploading the first file because it loops around the CreateTextFile method. This method is failing after the first attempt because the file already exists. What you really ought to do is loop around adding your files to the temporary file and then execute FTP only once. Your web server will appreciate the break as well.
Dim fso, folder, files, strPath
Set fso = CreateObject("Scripting.FileSystemObject")
strPath = "E:/Test"
Set folder = fso.GetFolder(strPath)
Set files = folder.Files
Const hostname = "ftp.domain.com"
Const port = 21
Const username = "username"
Const password = "password"
Const remoteDir = "/"
Const useDefaultsExclusively = True
Const skipConfirmation = True
tempDir = shell.ExpandEnvironmentStrings("%TEMP%")
' temporary script file supplied to Windows FTP client
scriptFile = tempDir & "\" & fso.GetTempName
' temporary file to store standard output from Windows FTP client
outputFile = tempDir & "\" & fso.GetTempName
Set textFile = fso.CreateTextFile(scriptFile, True)
'input script
textFile.WriteLine("open " & hostname & " " & port)
textFile.WriteLine("user " & username)
textFile.WriteLine(password)
textFile.WriteLine("cd " & Chr(34) & remoteDir & Chr(34))
textFile.WriteLine("binary")
textFile.WriteLine("prompt n")
For Each item In files
If InStr(1, item.Name, "txt") <> 0 Then
textFile.WriteLine("put " & Chr(34) & item.Path & "\" & item.Name & Chr(34))
End If
Next
textFile.WriteLine("quit")
textFile.Close
Set shell = CreateObject("WScript.Shell")
' bWaitOnReturn set to TRUE - indicating script should wait for the program
' to finish executing before continuing to the next statement
shell.Run "%comspec% /c FTP -n -s:" & scriptFile & " > " & outputFile, 0, True
WScript.Sleep 10
' open standard output temp file read only, failing if not present
Set textFile = fso.OpenTextFile(outputFile, 1, 0, -2)
results = textFile.ReadAll
textFile.Close
fso.DeleteFile(scriptFile)
fso.DeleteFile(outputFile)