Hello stackoverflow community,
I am writing a batch file to do some automatic computer maintenance and have included several antivirus applications. For some reason, the third "if not" statement is never reached.
:AV
REM MSE
if not '%MSE%'=='' (
Echo Scanning for viruses using Microsoft Security Essentials.
Echo.
%MSE% -Scan -ScanType 1
Echo.
GOTO Defrag
)
REM AVG
if not '%AVG%'=='' (
Echo Scanning for viruses using AVG.
Echo.
%AVG% /COMP /QT /TRASH
Echo.
GOTO Defrag
)
REM NOD32
if not '%NOD32%'==''(
Echo Scanning for viruses using NOD32.
Echo.
if '%NOD32%'=='' GOTO NOD32NotFound
%NOD32% /aind /auto /log-file="%userprofile%\Desktop\Scan_Results.txt"
Echo.
GOTO Defrag
)
REM If all else fails...
GOTO AVNotFound
Currently, there are three blocks of codes, one for each antivirus program. Each block of code is executed only when the variable %AVG% %MSE% or %NOD32% is not empty, meaning they point to a valid file. I assign the variables using the code:
if exist "%programfiles(x86)%\AVG\AVG2012\avgscana.exe" set AVG="%programfiles(x86)%\AVG\AVG2012\avgscana.exe"
All three blocks of code works perfectly, nothing is wrong with the coding. The problem is that whatever the third block is, it never executes. So in the current example, the blocks of code go in the order of MSE, AVG, and NOD32. NOD32's block of code does not execute because it's the third block. Conversely, if I cut and paste the blocks into another order with AVG's block of code being the third block, it would not execute.
Any ideas?
Any suggestions?
Edited for clarification.
You're missing a space in the line:
if not '%NOD32%'==''(
Try:
if not '%NOD32%'=='' (
When I tried the script this line caused a failure. After changing the line, it worked.
Are the variables %MSE%, %AVG% or %NOD32% batch files? If yes, you will need to invoke them using "call" instead (for example call %AVG%)
If you call a batch file from another, the first one will exit after executing the 2nd one, unless it is called with "call".
Do any of your %AVG%, %NOD32%, or %MSE% variables have brackets in them? Could they be in the C:\Program Files (x86)\ path? A bracket will close the branch prematurely.
Place quotes around the executable part of the commands, for example:
"%MSE%" -Scan -ScanType 1
Related
A bit embarrased that my first question is about a simple batch file, but my knowledge is quite limited in this topic.
I am writing a simple batch script to copy some data from a to b. For this reason i want to create destination folders according to the current month and check if the folder already exists.
I am not able to identify why my code exits at line 14 without throwing anything after pressing a key. I also tried verifying the code with a batch code verifying tool (BatCodeCheck "quite outdated"). However it throws no errors or warnings regarding my problem.
The code in question:
#echo off
echo Getting current month...
set month=%date:~3,2%
if %month:~0,1% == 0 (
set /A month=%month:~1,2%-1
) ELSE (
set /A month=%month%-1
)
if [%month:~1,2%] == [] (
set month=0%month%
)
echo "%month% is the month before"
echo Checking for monthly folder...
pause
if exist %~dp0%month%\ (
echo "Folder already exists. Press y to overwrite"
pause
set /p Input=Overwrite? (y/n):
if /I "%Input%"NEQ"y" (
EXIT 0
)
) ELSE (
echo "Folder doesn't exist already. Creating..."
mkdir %~dp0%month%\
)
The check log:
Time : 2022-05-12 19:10:08
Program : BatCodeCheck, Version 0.38
Arguments : D:\RunBackup.bat /L /W
File name : "D:\RunBackup.bat"
File date : 2022-05-12 19:10:02
File encoding : ASCII
Tests : ABELMSUV
A = command line Arguments for batch commands
B = Best practice tips
E = Environment variables
L = Labels
M = common Mistakes
S = verbose Summary (variables, labels, subroutines)
U = Undefined environment variables
V = Vulnerabilities
RISKY CODE:
Line 18: SET /P could make your code vulnerable to exploits (see http://www.robvanderwoude.com/battech_inputvalidation.php#SetP)
SUMMARY:
1 line generated a warning and should be examined
Note that some warnings are only displayed once. Correct the errors and run this test again.
Hopefully its not a syntax error i am missing...
When you use the point-click-and-giggle method of executing a batch, the batch window will close if a syntax-error is found or the script runs to completion. You can put a pause after statements and home in on the error, but better to open a 'command prompt' and run your batch from there so that the window remains open and any (error) messages will be displayed.
I suspect if exist %~dp0%month%\ ( should be quoted if exist "%~dp0%month%\" ( - the path may contain spaces.
Same goes for mkdir - without the quotes, each space-separated string will be regarded as a directory-name to create.
Your comparison statement "%Input%"NEQ"y"requires spaces each side of neq.
Read Stephan's DELAYEDEXPANSION link for why this won't work as-is. There are many, many SO articles about how to use choice in this situation.
I have a batch script that checks the input of the user before running a specific block of code. Both blocks manipulate the same variables but it appears the variables of the second block are the ones that are being set even if I choose the first block to execute.
Code:
if %x == 1 goto :Block1
if %x == 2 goto :Block2
:Block1
set variable== "Works"
:Block2
set variable == "Works"
Block 2 is executing even if I input 1. Am I missing something?
Unlike many languages, batch has no concept of the end of a "procedure" - it simply continues execution line-by-line until it reaches the end-of-file. Consequently, you need to goto :eof after completing the mainline, otherwise execution will continue through the subroutine code. :EOF is a predefined label understood by CMD to mean end of file. The colon is required.
I run a .tcl script using a .bat file as per the simplified example below:
script.tcl
set a "this is the script"
puts $a
script.bat
echo off
set tclpath="C:\tclsh.exe"
set filepath="C:\script.tcl"
%tclpath% %filepath%
I wonder whether I can include in the .bat file the commands of the .tcl script, so instead of having two files I just have one .bat file that runs tclsh.exe and passes the commands to the tcl shell.
Is this possible? and how can I do it?
For many things, there are pages on the Tcler's Wiki that can be looked to for interesting things. In particular, this old page has some really useful techniques. As you read through, you'll see a history of techniques tried. They depend on the fact that Tcl commands can be prefixed with :: usually (marking them as a weird label in the batch file language) and you can comment out blocks of code in Tcl with if 0 (with Tcl not parsing the contents, beyond ensuring that it is brace-balanced, which code usually is).
The best technique is one that doesn't just make the code multilingual, but also makes it easily readable. Preserving readability is the key to not going crazy.
::if 0 {
#rem This code in here is pure batch script
echo off
set tclpath="C:\tclsh.exe"
%tclpath% "%~f0" %*
#rem Put this at the end; it means terminate since the “eof” label is end-of-file
#goto eof
}
# This code is the pure Tcl code
set a "this is the script"
puts $a
The other bits to be aware of:
"%~f0" — This gets the full path to the “zero-th argument”, which is the name of the script you're running.
%* — This is all the remaining arguments. It's a good idea to pass them on, and you can access them from Tcl using the list in the global argv variable.
I wonder whether I can include in the .bat file the commands of the .tcl script, so instead of having two files I just have one .bat file
that runs tclsh.exe and passes the commands to the tcl shell.
Easy peasy. . .
You can use a CALL to a subroutine in a batch script that will append the commands to the dynamically created script file which you specify with the set filepath variable.
This way you have everything in the batch script and you do not need to worry about the tcl script file other than ensuring the :tclshScript routine that creates it has the correct syntax, etc.
You essentially build the tcl script logic with batch ECHO commands and it'll create it per run.
Use caution with special characters though as the carat ^ symbol may be needed to escape certain character to the tcl script if batch interprets those otherwise or you notice an issue.
echo off
set tclpath="C:\tclsh.exe"
set filepath="C:\script.tcl"
IF EXIST "%filepath%" DEL /Q /F "%filepath%"
CALL :tclshScript
%tclpath% %filepath%
EXIT
:tclshScript
ECHO set a "this is the script">>%filepath%
ECHO puts $a>>%filepath%
GOTO EOF
Further Resources
CALL
ECHO
Escape
To chcek if file is loaded or not, I load its contents using:
set /p filevar=file.txt
and check if var is empty:
if "%filevar%"="" exit
When script chceks file with multiple lines, execution of script stops, so i suppose that chcecking fails. Why script fails? How to perform such check properely?
Firstly, you've got the syntax of your set /p command messed up. As it is, it will prompt the user with the text "file.txt". I think what you mean is
set /p "filevar=" <file.txt
You should also use the /b switch with exit to prevent the console window from closing if your script is run from the command line.
But yeah, as jeb states, to check whether left equals right, either use == or equ. Or, as dbenham reminds me, if you're checking for an empty value, you can also use if not defined.
if "%filevar%"=="" exit /b
if "%filevar%" equ "" exit /b
if not defined filevar exit /b
All three statements will have the same result1.
In a console window, enter help if for more information.
1 It's worth mentioning that, while those three if statements have the same result outside of a parenthetical code block, only the third one will work reliably within parentheses (such as in a for loop). The first two would need delayed expansion to work properly if used within the same code block as %filevar% is set.
I've been investigating this a lot with threads on StackOverflow and the like, but although I feel I'm close to the solution, this problem is giving me headaches.
What I'm trying to do: When a specific external hard drive is connected (distinguished via VolumeSerialNumber over WMIC), the drive letter is found out, and mirroring is done via robocopy. The script is executed via double-click. This is what I have so far:
FOR /F "skip=1" %%i in ('wmic logicaldisk where VolumeSerialNumber^="XXXXXXXX" get deviceid 2^>nul') DO (
SET y=%%i
IF [%y%]==[] GOTO hdmissing
SET "backuphd=%%i"
GOTO endfor
)
:endfor
robocopy "C:\Users\Herbert\Documents" "%backuphd%\Backup\Documents" /MIR
ECHO Backup done
ECHO end
:hdmissing
ECHO Couldn't find external drive
:end
PAUSE
This way, the external HD is never detected (%y% is always an empty string). However, if I execute the script twice in the same console session, everything works as expected. But I want it to work at the first execution.
This is what I've tried so far:
Put SET y=dummy at the beginning of the script. The HD is always found, triggering a backup to C: if the HD is not actually connected (apparently SET y=%%i doesn't alter y?)
Change %y% to !y! - The HD is always found, again
Generation 3,576 of the delayed expansion problem, compounded by a contaminated environment.
There's no setlocal apparent, so y remains set in the environment after the first run - hence the 'later run characteristics different from first run' phenomenon.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
The key in your case appears to be no setlocal enabledelayeexpansion and !y! - because !y! is just that - a literal string !y! unless delayedexpansion is invoked by the setlocal command.
(having said that,
IF [%%i]==[] GOTO hdmissing
would work just as well, as would
SET "y=%%i"
IF not defined y GOTO hdmissing
because if [not] defined var operates on the run-time value of var. "quoting the set arguments" ensures that any stray trailing spaces on the line are not included in the value assigned
)
As Magoo already pointed out, !y! doesn't work, since I forgot to enable Delayed Expansion. However, enabling it requires you to escape certain characters, which seemed quite irritating and tedious to me. It could be possible to just enclose the command in the FOR-loop with double quotes, as done in the two edits of this question, but I found out after solving it differently.
What I did was moving the block
IF [%y%]==[] GOTO hdmissing
SET "backuphd=%%i"
after :endfor. This way, it's outside of the for loop and %y% gets expanded accordingly.
Mind that this solution works only because I need just one item, and because of the programming flow.
Other useful approaches might be found in this question.
However, if you want to do the same thing: easier (though not as robust) solutions without WMIC might include
Putting a file with a specific name on the hard drive and checking for it with IF EXIST before backing up, hoping the user will not delete it, and the HD is connected to the same drive letter.
Putting the batch script on the drive itself and work with relative path names (optional: put a shortcut on the Desktop and hope again the drive is always under the same letter)