I've tried to write a batchfile, which would check it's own directory for other batch files and check these for a certain text. If the text is found, I want the program to jump to the end, otherwise copy itself into the found file. Here's how I tried:
rem windowsisajoke
for %%f in (*.bat) do (set A=%%f)
set FILE=%A%
set CONTENT=windowsisajoke
findstr /i "%CONTENT%" %FILE% >NUL
if errorlevel 0 goto end
copy %0 %A%
:end
if errorlevel 0 actually means "if errorlevel is zero or greater". (I know - very intuitive...)
Either change your logic:
if errorlevel 1 copy %0 %A%
or use
if %errorlevel%==0 goto :end
Related
I would like to create a conditional statement for xcopy that does something only if xcopy copies something.
So basically what I am saying is, if xcopy copies a file, do something.
If not do something else.
How this be done using a batch?
So far I have the following:
xcopy "Z:\TestFiles.zip" "C:\Test\" /d /y
if xcopy exit code 0 (
)else
UPDATE:
When running the following script:
xcopy /d /y "Z:\TestFiles.zip" "C:\Testing\"
echo %errorlevel%
The following are the results I get:
1 File(s) copied
C:\Users\jmills\Desktop>echo 0
0
_
0
File(s) copied
C:\Users\jmills\Desktop>echo 0
0
Because both error codes come out to 0 I cannot use:
IF ERRORLEVEL 1 GOTO FilesCopied
IF ERRORLEVEL 0 GOTO NoFiledCopied
:NoFiledCopied
REM do something
GOTO eof
:FilesCopied
REM do something
GOTO eof
:eof
You could use the conditional execution operators && and ||:
xcopy /D /Y "Z:\TestFiles.zip" "C:\Test\" && echo success. || echo Failure!
Alternatively, you could check the ErrorLevel value:
xcopy /D /Y "Z:\TestFiles.zip" "C:\Test\"
rem // The following consition means 'if ErrorLevel is greater than or equal to 1':
if ErrorLevel 1 (
echo Failure!
) else (
echo Success.
)
This works because xcopy does not return a negative ErrorLevel value.
Or you could query the value of the %ErrorLevel% pseudo-variable:
xcopy /D /Y "Z:\TestFiles.zip" "C:\Test\"
if %ErrorLevel% equ 0 (
echo Success.
) else (
echo Failure!
)
Note that if the above code is placed within a (parenthesised) block of code, you need to enable and apply delayed variable expansion to get the latest !ErrorLevel! value.
According to your update, you want to detect whether or not xcopy copied any files. As per this related Super User thread, xcopy never returns an exit code of 1 (which I consider a design flaw), contrary to the documentation, even if the /D option is used and no files are copied.
To circumvent this you could capture the returned summary message (# File(s)) by a for /F loop, extract the number (#) and check whether it is greater than 0. The exit code should still be checked though as there might occur other exceptions:
rem // Initialise variable:
set "NUM=0"
rem /* Use a `for /F` loop to capture the output of `xcopy` line by line;
rem the first token is stored in a variable, which is overwritten in
rem each loop iteration, so it finally holds the last token, which is
ewm nothing but the number of copied files; if `xcopy` fails, number `0`
rem is echoed, which is then captured as well: */
for /F "tokens=1" %%E in ('
2^> nul xcopy /D /Y "Z:\TestFiles.zip" "C:\Test\" ^|^| echo 0
') do (
rem // Capture first token of a captured line:
set "NUM=%%E"
)
rem // Compare the finally retrieved count of copied files:
if %NUM% gtr 0 (
echo Success.
) else (
echo Failure!
)
Regard that the captured summary line is language-dependent, so the token to extract, as well as the echoed failure text (0), might need to be adapted accordingly.
You could use robocopy instead of xcopy:
ROBOCOPY "Z:\\" "C:\Test\\" "TestFiles.zip"
IF ERRORLEVEL 1 GOTO FilesCopied
IF ERRORLEVEL 0 GOTO NoFiledCopied
:NoFiledCopied
REM do something
GOTO eof
:FilesCopied
REM do something
GOTO eof
:eof
Further information about robocopy:
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
I have a batch file with 10 lines and 5 functions in a batch script. How can I ensure that all the commands in a batch file are successful.
In other way, what's the logic to calculate return code of each command at the end of a script.
1. #ECHO OFF
2. if not exist "%Destination%\%NAME%" md %Destination%\%NAME%
3. if not exist "%Destination%\%NAME2%" md %Destination%\%NAME2%
4. rmdir %Destination%\%NAME3%
5. if not exist "%Destination%\NAME4%" md %Destination%\%NAME4%
6. cd /d X:\test1
in the above 5 lines, 4th line returns %ERRORLEVEL% 1 and 6th line returns the same. But, I could not put IF %ERRORLEVEL%==0 after every command. So, how could i script to handle this.
You should firstly save your file as .cmd instead of .bat for better error handling. Also always enclose your paths with double quotes. Then I suggest you test existance as well to overcome errorlevel.
If exist "%Destination%\%NAME3%" rmdir "%Destination%\%NAME3%"
For the code example I suggest following:
#echo off
rem Verify the existence of all used environment variables.
for %%I in (Destination NAME NAME2 NAME3 NAME4) do (
if not defined %%I (
echo Error detected by %~f0:
echo/
echo Environment variable name %%I is not defined.
echo/
exit /B 4
)
)
rem Verify the existence of all used directories by creating them
rem independent on existing already or not and next verifying if
rem the directory really exists finally.
for %%I in ("%Destination%\%NAME%" "%Destination%\%NAME2%") do (
md %%I 2>nul
if not exist "%%~I\" (
echo Error detected by %~f0:
echo/
echo Directory %%I
echo does not exist and could not be created.
echo/
exit /B 3
)
)
rem Remove directories independent on their existence and verify
rem if the directories really do not exist anymore finally.
for %%I in ("%Destination%\%NAME3%") do (
rd /Q /S %%I 2>nul
if exist "%%~I\" (
echo Error detected by %~f0:
echo/
echo Directory %%I
echo still exists and could not be removed.
echo/
exit /B 2
)
)
cd /D X:\test1 2>nul
if /I not "%CD%" == "X:\test1" (
echo Error detected by %~f0:
echo/
echo Failed to set "X:\test1" as current directory.
echo/
exit /B 1
)
This batch file handles nearly all possible errors which could occur during execution of this batch file. A remaining problem could be caused by an environment variable containing one or more double quotes in its value. The solution would be using delayed expansion.
Linux shell script interpreters have the option -e to exit immediately execution of a script if any command or application returns with a value not equal 0. But Windows command interpreter cmd.exe does not have such an option. The options of cmd.exe can be read on running in a command prompt window cmd /?.
So it is necessary to use in a batch file:
if exist "..." exit /B 1 or goto :EOF
if not exist "..." exit /B 1 or goto :EOF
if errorlevel 1 exit /B 1 or goto :EOF
... || exit /B 1 or ... || goto :EOF
See also the Stack Overflow articles:
Windows batch files: .bat vs .cmd?
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file
What are the ERRORLEVEL values set by internal cmd.exe commands?
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
The batch has to remove files and directories from specific locations and output success or stdout/stderr messages to a new .txt file. I have created the most of the script and it performs exactly as it should, except when the deletion is successful it moves forward to the next line rather than echo a 'successful' message on the log.
echo Basic Deletion Batch Script > results.txt
#echo off
call :filelog >> results.txt 2>&1
notepad results.txt
exit /b
:filelog
call :delete new.txt
call :delete newer.txt
call :delete newest.txt
call :remove c:\NoSuchDirectory
GOTO :EOF
:delete
echo deleting %1
del /f /q c:\Users\newuser\Desktop\%1
if errorlevel 0 echo succesful
GOTO :EOF
:remove
echo deleting directory %1
rmdir /q /s %1
GOTO :EOF
For some reason I can't find the syntax for if del succeeds echo 'successful'. In the above example if I remove the line
if errorlevel 0 echo successful
Everything works fine, but no success message. With this line left in it echoes success for every line.
del and ErrorLevel?
The del command does not set the ErrorLevel as long as the given arguments are valid, it even resets the ErrorLevel to 0 in such cases (at least for Windows 7).
del modifies the ErrorLevel only in case an invalid switch is provided (del /X sets ErrorLevel to 1), no arguments are specified at all (del sets ErrorLevel to 1 too), or an incorrect file path is given (del : sets ErrorLevel to 123), at least for Windows 7.
Possible Work-Around
A possible work-around is to capture the STDERR output of del, because in case of deletion errors, the related messages (Could Not Find [...], Access is denied., The process cannot access the file because it is being used by another process.) are written there. Such might look like:
for /F "tokens=*" %%# in ('del /F /Q "\path\to\the\file_s.txt" 2^>^&1 1^> nul') do (2> nul set =)
To use the code in command prompt directly rather than in a batch file, write %# instead of %%#.
If you do not want to delete read-only files, remove /F from the del command line;
if you do want prompts (in case wildcards ? and/or * are present in the file path), remove /Q.
Explanation of Code
This executes the command line del /F /Q "\path\to\the\file_s.txt". By the part 2>&1 1> nul, the command output at STDOUT will be dismissed, and its STDERR output will be redirected so that for /F receives it.
If the deletion was successful, del does not generate a STDERR output, hence the for /F loop does not iterate, because there is nothing to parse. Notice that ErrorLevel will not be reset in that case, its value remains unchanged.
If for /F recieves any STDERR output from the del command line, the command in the loop body is executed, which is set =; this is an invalid syntax, therefore set sets the ErrorLevel to 1. The 2> nul portion avoids the message The syntax of the command is incorrect. to be displayed.
To set the ErrorLevel explicitly you could also use cmd /C exit /B 1. Perhaps this line is more legible. For sure it is more flexible because you can state any (signed 32-bit) number, including 0 to clear it (omitting the number clears it as well). It might be a bit worse in terms of performance though.
Application Example
The following batch file demonstrates how the above described work-around could be applied:
:DELETE
echo Deleting "%~1"...
rem this line resets ErrorLevel initially:
cmd /C exit /B
rem this line constitutes the work-around:
for /F "tokens=*" %%# in ('del /F /Q "C:\Users\newuser\Desktop\%~1" 2^>^&1 1^> nul') do (2> nul set =)
rem this is the corrected ErrorLevel query:
if not ErrorLevel 1 echo Deleted "%~1" succesfully.
goto :EOF
Presetting ErrorLevel
Besides the above mentioned command cmd /C exit /B, you can also use > nul ver to reset the ErrorLevel. This can be combined with the for /F loop work-around like this:
> nul ver & for /F "tokens=*" %%# in ('del /F /Q "\path\to\the\file_s.txt" 2^>^&1 1^> nul') do (2> nul set =)
Alternative Method Without for /F
Instead of using for /F to capture the STDERR output of del, the find command could also be used like find /V "", which returns an ErrorLevel of 1 if an empty string comes in and 0 otherwise:
del "\path\to\the\file_s.ext" 2>&1 1> nul | find /V "" 1> nul 2>&1
However, this would return an ErrorLevel of 1 in case the deletion has been successful and 0 if not. To reverse that behaviour, an if/else clause could be appended like this:
del "\path\to\the\file_s.ext" 2>&1 1> nul | find /V "" 1> nul 2>&1 & if ErrorLevel 1 (1> nul ver) else (2> nul set =)
Different Approach: Checking File for Existence After del
A completely different approach is to check the file for existence after having tried to delete it (thanks to user Sasha for the hint!), like this, for example:
del /F /Q "\path\to\the\file_s.txt" 1> nul 2>&1
if exist "\path\to\the\file_s.txt" (2> nul set =) else (1> nul ver)
When using this syntax, instead of this
if errorlevel 0 echo successful
you can use this - because errorlevel 0 is always true.
if not errorlevel 1 echo successful
Just use rm from UnxUtils (or gow or cygwin). It sets the errorlevel correctly in case of a nonexistent file, or any errors deleting the file.
This was added as an edit by the original asker, I have converted it to a community wiki answer because it should be an answer, not an edit.
I found out how to do it... one way anyway.
echo Startup > results.txt
#echo off
call :filelog >> results.txt 2>&1
notepad results.txt
exit /b
:filelog
call :delete new.txt
call :delete newer.txt
call :delete newest.txt
call :remove c:\NoSuchDirectory
GOTO :EOF
:delete
echo deleting %1
dir c:\users\newuser\Desktop\%1 >NUL 2>&1
SET existed=%ERRORLEVEL%
del /f /q c:\Users\newuser\Desktop\%1
dir c:\users\newuser\Desktop\%1 2>NUL >NUL
if %existed% == 0 (if %ERRORLEVEL% == 1 echo "successful" )
GOTO :EOF
:remove
echo deleting directory %1
rmdir /q /s %1
GOTO :EOF
IF ERRORLEVEL 0 [cmd] will execute every time because IF ERRORLEVEL # checks to see if the value of ERRORLEVEL is greater than or equal to #. Therefore, every error code will cause execution of [cmd].
A great reference for this is: http://www.robvanderwoude.com/errorlevel.php
>IF /?
Performs conditional processing in batch programs.
IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command
NOT Specifies that Windows should carry out
the command only if the condition is false.
ERRORLEVEL number Specifies a true condition if the last program run
returned an exit code equal to or greater than the number
specified.
I would recommend modifying your code to something like the following:
:delete
echo deleting %1
del /f /q c:\Users\newuser\Desktop\%1
if errorlevel 1 (
rem This block executes if ERRORLEVEL is a non-zero
echo failed
) else (
echo succesful
)
GOTO :EOF
If you need something that processes more than one ERRORLEVEL, you could do something like this:
:delete
echo deleting %1
del /f /q c:\Users\newuser\Desktop\%1
if errorlevel 3 echo Cannot find path& GOTO :delete_errorcheck_done
if errorlevel 2 echo Cannot find file& GOTO :delete_errorcheck_done
if errorlevel 1 echo Unknown error& GOTO :delete_errorcheck_done
echo succesful
:delete_errorcheck_done
GOTO :EOF
OR
:delete
echo deleting %1
del /f /q c:\Users\newuser\Desktop\%1
goto :delete_error%ERRORLEVEL% || goto :delete_errorOTHER
:delete_errorOTHER
echo Unknown error: %ERRORLEVEL%
GOTO :delete_errorcheck_done
:delete_error3
echo Cannot find path
GOTO :delete_errorcheck_done
:delete_error2
echo Cannot find file
GOTO :delete_errorcheck_done
:delete_error0
echo succesful
:delete_errorcheck_done
GOTO :EOF
The answer of aschipfl is great (thanks, helped me a lot!) using the code under Presetting ErrorLevel you get a nice standard function:
Take care to use %~1 instead of %1 in the del statement, or you will get errors if you use a quoted filename.
::######################################################################
::call :DELETE "file.txt"
::call :DELETE "file.txt" "error message"
:DELETE
>nul ver && for /F "tokens=*" %%# in ('del /F /Q "%~1" 2^>^&1 1^> nul') do (2>nul set =) || (
if NOT .%2==. echo %~2
)
goto :EOF
BTW 1: You can give a nifty error message as a second parameter
BTW 2: Using :: instead of REM for comments makes the code even more readable.
Code:
Error Code: (What you did)
if errorlevel 0 echo succesful
The problem here is that you aren't calling errorlevel as a variable and plus you didn't add in the operator to the statement as well.
Correct Code: (Here is what it should actually be.)
if %ERRORLEVEL% EQU 0 echo succesful
Definitions:
EQU: The EQU stands for Equal. This kind of operator is also called a relational operator. Here is the documentation link to operators if you wanna know more, there are other ones but this helped me.
ERRORLEVEL: is declared as a variable and usually get the error level of the last command run usually. Variables are usually called when they are between percent signs like this
%foo%
For some more help on variables, go to cmd (Which you can go to by searching it on windows 10) and type in "set /?", without the quotes. the set command is the command you use to set variables
I wrote the following batch to do the following steps:
Check if a file on a server is opened by another user
make a backup of the file
open the file
2>nul ( >>test.xlsx (call )) if %errorlevel% == 1 goto end
#echo off
rem get date, make if file name friendly
FOR /F "tokens=1-4 delims=/ " %%i in ('date/t') do set d=%%j-%%k-%%l#%%i#
rem get time, make if file name friendly
FOR /F "tokens=1-9 delims=:. " %%i in ('time/t') do set t=%%i_%%j_%%k%%l
set XLSX=%d%%t%.xlsx
ren Test.xlsx %xlsx%
xcopy *.xlsx J:\Test\Backup
ren %xlsx% Test.xlsx
call Test.xlsx
:end
The problem is, that the line wich tries to check if the file is locked does not work on the server.
Can anybody help me to find the mistake in my batch?
If you write
2>nul ( >>test.xlsx (call )) if %errorlevel% == 1 goto end
you get an error. if is not expected. Two instructions in the same line without separation.
If it is converted into
2>nul ( >>test.xlsx (call )) & if %errorlevel% == 1 goto end
Then the problem is delayed expansion. The %errorlevel% variable is replaced with its value when the line is parsed and at this time, the first part of has not been executed, so no errorlevel is set.
If you change it to
2>nul ( >>test.xlsx (call ))
if %errorlevel% == 1 goto end
it will work
For a more concise construct, you can try this
(>>test.xlsx call;) 2>nul || goto end
Same function, less code.
I am writing a batch file to upgrade some systems. I need to parse a date from an xml file and save it for use later in the file. The format of the date is yyyy\MM\dd.
What I have so far is:
#echo off
setLocal DisableDelayedExpansion
for /f "tokens=* delims= " %%G in (ConnectionManagement.xml) do (
set str=%%G
set mydate=%%G
echo got-
echo %%G
echo %mydate%
PAUSE
ECHO %mydate%|findstr /R /C:[0-9][0-9][0-9][0-9]\\[0-9][0-9]\\[0-9][0-9] > nul
IF ERRORLEVEL 0 goto valueok
)
echo DONE
PAUSE
goto end
:valueok
echo VALUEOK
:end
PAUSE
Unfortunately this incorrectly recognised the xml header as a valid date; but I think this is to do with ErrorLevel being reset (?). mydate isn't being set, and it recognises the empty variable mydate as a match (!!??). The output is:
got-
<?xml version="1.0" encoding="utf-8"?>
ECHO is off.
Press any key to continue . . .
VALUEOK
Press any key to continue . . .
...
Getting rather desperate for a solution. thanks...
ErrorLevel is not being reset. What is actually happening is that IF ERRORLEVEL 0 does not behave the way you expect. Basically, the test is not "Does errorlevel equal 0?", its "Is errorlevel greater than or equal to zero?". Given this, it should be clear that IF ERRORLEVEL 0 will always be true (at least, if you only expect positive errors...)
Therefore, you need to use IF NOT ERRORLEVEL 1 goto valueok instead.
As RB wrote, IF ERRORLEVEL 0 does not behave the way you expect.
The if errorlevel is a special IF-case.
To use a normal IF-construct you could use
setlocal EnableDelayedExpansion
...
FOR-LOOP .. DO (
if !ERRORLEVEL! EQU 0 goto valueok