I made a piece of batch-code, and I thought this will work. What I'm thinking that this code is doing? I have some plugins and I want to test if the deploy correct. So I get the pluginlink from the plugins.txt. Then I get the plugin from SVN with the java sentence. I deploy the plugin and get the feedback in test1.txt. Then I do a findStr in that file and searchs for "BUILD SUCCESSFUL" if it is there I want to add the sentence Build Gelukt and if it fails I want to add Build Fout. But I get always the answer Build Gelukt, while as you can see in the image he sends back that the build is Failed.
Whats wrong with this piece of code?
for /f "tokens=* delims= " %%a in (plugins.txt) do (
echo %%a
cd "C:\dotCMS Automatic Install"
java -cp .;"C:\dotCMS Automatic Install\svnkit.jar" Test %%a
cd %dotcms_home%
call ant deploy-plugins > test1.txt
FindStr "SUCCESSFUL" test1.txt
if %ERRORLEVEL% ==1 (echo ^<tr BGCOLOR=\"#FFFFFF\"^>^<td^>%%a^</td^>^<td^>Build Fout^</td^>^</tr^> >> C:\dotCMSResults\goedje.html ) else (echo ^<tr BGCOLOR=\"#00FF00\"^>^<td^>%%a^</td^>^<td^>Build Gelukt^</td^>^</tr^> >> C:\dotCMSResults\goedje.html)
del test1.txt
rem call ant undeploy-plugins >> test.txt
)
Classic batch problem - you are setting your ERRORLEVEL and attempting to access it using %ERRORLEVEL% within the same DO() clause. %VAR% expansion happens at parse time, and the entire FOR ... DO() statement is parsed once, so you are seeing the value of ERRORLEVEL before the statement was executed. Obviously that won't work.
jeb alluded to the answer in his comment regarding disappearing quotes. Your problem will be fixed if you setlocal enableDelayedExpansion at the top, and then use !ERRORLEVEL! instead of %ERRORLEVEL%. Also, GregHNZ is correct in that the ERRORLEVEL test should occur immediately after your FINDSTR statement.
There are other ways to handle ERRORLEVEL within parentheses that don't require delayed expansion:
The following tests if ERRORLEVEL is greater than or equal 1
IF ERRORLEVEL 1 (...) ELSE (...)
And below conditionally executes commands based on the outcome of the prior command
FindStr "SUCCESSFUL" test1.txt && (
commands to execute if FindStr succeeded
) || (
commands to execute if prior command failed.
)
The %ErrorLevel% variable applies to the immediately previous command only.
So when you do this:
echo Errorlevel: %ERRORLEVEL%
With your current code, you are getting the error level of the CD command above
Try putting your if %ERRORLEVEL% ==1 line immediately after the FindStr command, and then do the del and the cd afterward. Obviously you'll need to put the full path to the html file in your echo statement.
Related
i am trying to run below snippet of code on my windows server.
#echo off
set BRANCH_NAME_ID=compiler_branch
if %BRANCH_NAME_ID%==compiler_branch ( echo INSIDE COMPILER BRANCH )
echo %BRANCH_SHORT_ID%|findstr /r "^[r][0-9][0-9]*_00$" & IF %ERRORLEVEL% == 0 ( echo IN IF ) ELSE ( echo INFO else)
pause
I was expecting the only output should be INSIDE COMPILER BRANCH because, BRANCH_NAME_ID variable is referring to compiler_branch. But some reason i am also getting IN IF as well.
Ouptut:-
INSIDE COMPILER BRANCH
IN IF
Press any key to continue . . .
As per the document https://ss64.com/nt/findstr.html i notice below and wrote the script accordingly. But some reason %ERRORLEVEL% is setting to 0 in line3 of my code thought the string is not matching :-
FINDSTR will set %ERRORLEVEL% as follows:
0 A match is found in at least one line of at least one file.
1 If a match is not found in any line of any file, (or if the file is not found at all).
2 Wrong syntax
An invalid switch will only print an error message in error stream.
Am i missing something ?
Because of how the interpreter reads files (see How does the Windows Command Interpreter (CMD.EXE) parse scripts? for a massive amount of info), %ERRORLEVEL% in that line gets replaced with its current value before the line is actually run. In order to have the command run and then have the value checked correctly, put the if statement on its own line.
#echo off
set BRANCH_NAME_ID=compiler_branch
if %BRANCH_NAME_ID%==compiler_branch ( echo INSIDE COMPILER BRANCH )
echo %BRANCH_SHORT_ID%|findstr /r "^[r][0-9][0-9]*_00$"
IF %ERRORLEVEL% == 0 ( echo IN IF ) ELSE ( echo INFO else)
pause
If for some reason you absolutely insist on using & to chain commands together (there is no reason to ever do this and it only makes things worse imo), then you can enable delayed expansion and use !ERRORLEVEL! instead.
#echo off
setlocal enabledelayedexpansion
set BRANCH_NAME_ID=compiler_branch
if %BRANCH_NAME_ID%==compiler_branch ( echo INSIDE COMPILER BRANCH )
echo %BRANCH_SHORT_ID%|findstr /r "^[r][0-9][0-9]*_00$" & IF !ERRORLEVEL! == 0 ( echo IN IF ) ELSE ( echo INFO else)
pause
How can I execute code saved in a variable? I'm trying to execute an if statement that I have saved in another file (that must remain a text file). Clues on how to execute an if statement just from a variable might help, as I assume the problem is that it can't read the %%s.
Text file contains:
if %var%==0301 (echo Yay)
Batch file contains:
for /f "tokens=*" %%s in (code.file) do (
%%s
)
This normally executes the code in code.file by setting everything in code.file to the variable %%s and then executing the variable.
The result is this: 'if' is not recognized as an internal or external command, operable program or batch file.
This method works for executing echo and set, but I need it to work for if.
The IF is detected by the parser in phase2 only, but %%s will be expanded in a later phase.
You need to use a percent expansion for it, like in this sample
for /f "tokens=*" %%s in (code.file) do (
set "line=%%s"
call :executer
)
exit /b
:executer
%line%
exit /b
But for your sample: if %var%==0301 (echo Yay)
It will never say Yay, because %var% will never be expanded.
This is because %line% is already a percent expansion, it will not work recursively.
That could be solved by changing the text in code.file to
if !var! == 0301 (echo Yay)
This works, because the delayed expansion happens after the percent expansion
Or a much simpler solution:
copy code.file tmp.bat
call tmp.bat
del tmp.bat
The major problem at hand is that the command interpreter particularly handled the commands if, for and rem: These commands are recognized earlier than every other one, even before for meta-variables like %%s become expanded. Therefore, these commands are no longer detected after expansion of %%s.
Refer to: How does the Windows Command Interpreter (CMD.EXE) parse scripts?
According to this, said commands are detected during Phase 2, while expansion of for meta-variables happens in Phase 4. Other commands are found later in Phase 7.
A possible way to work around that is to use a sub-routine, which %-expansion occurs in, which happens in Phase 1, hence before recognition of the three special commands:
for /f "tokens=*" %%s in (code.file) do (
rem // Execute the read command in a sun-routine:
call :SUB %%s
)
goto :EOF
:SUB
rem // Take the whole argument string as a command line:
%*
goto :EOF
I am trying to convert a whole BATCH script to SHELL script with the help of this sort of converter manual.
I have almost finished, but I am struggling to convert this FOR LOOP:
for /f "tokens=*" %%a in ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') do (
if not errorlevel 1 goto :cannot_patch
)
I know that for /f is
Loop command: against a set of files - conditionally perform a command against each item.
However, as I am a noob to SHELL SCRIPT (and BASH as well), my best try was:
for -f "tokens=*" a in ( '$ADB shell mkdir /usr/ui/^|find /i "File exists"' ); do
if [ $? -nq 1 ]
then
cannot_patch
fi
done
which does not work, resulting in a Syntax error: Bad for loop variable.
Any hint, link, or suggestion would be very much appreciated.
EDIT
I am trying to understand what exactly ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') is doing.
I thought those were sh commands, but it turns out I was wrong and that find /i is trying to
Search(ing) for a text string in a file & display all the lines where it is found.
(https://ss64.com/nt/find.html)
| is the pipe operator and "File exists" should be the error thrown by mkdir in case the command tries to create a directory that already exists.
So I think I could probably write this easier, but still, what does the ^ symbol in /usr/ui/^ do? Is it a regex?
EDIT2
It seems indeed that #glenn_jackman is right: probably I'd better understand what the code is trying to do.
So to give a better context, here is a bit more code of the original batch:
for /f "tokens=*" %%a in ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') do (
if not errorlevel 1 goto :cannot_patch
)
:cannot_patch
echo Error: Cannot create directory!
echo Patch is already installed or system files exist and might be overwritten.
choice /m "Do you want to continue"
if errorlevel 2 goto :END
goto :continue_patch
To my understanding, the code is trying to run the adb shell mkdir command and, if it fails (throwing the "File exists" error), it will ask to the user if he/she wants to continue regardless.
So in this case, I guess the real problem is trying to write a code that does the same in SH, probably without the need of a for loop.
Still, I am finding it out...
for /f "tokens=*" %%a in ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') do (
if not errorlevel 1 goto :cannot_patch
)
runs the command %adb% shell mkdir /usr/ui/ then searches that output for the string File exists. if not errorlevel 1 means "if %errorlevel% is not greater than or equal to 1," which is a weird and awkward way of just saying if "%errorlevel%"=="0", which would mean that the string was found. If it's found, the script then goes to the label :cannot_patch.
In bash, the way to process the output of a command is to use $() instead of for /f, so
$(${adb} shell mkdir /usr/ui/ | grep "File exists") && cannot_patch
This assumes that the variable adb is set and you've got a function in your shell script somewhere called cannot_patch.
Note that the only difference between my answer and Paul Hodge's answer is that I'm assuming that you still need ADB to be called.
The general syntax of your loop matches https://ss64.com/nt/for.html
You should also look over https://ss64.com/nt/errorlevel.html and this SO reference that lists/explains error codes.
From the SO article, FOR /F │ 1 = No data was processed.
But the errorlevel page says
IF ERRORLEVEL 1 will return TRUE whether the errorlevel is 1 or 5 or 64
and the SO article also says
if %errorlevel% equ 5 (
echo Can not access the directory, check rights
)
Apparently ^ is a CMD escape character, used here to escape the pipe character being passed as part of a compound command that (I'm guessing...) creates a directory and checks for a failure, so I'm parsing this as an attempt to make the directory, detect if it already exists, and respond accordingly.
I think in bash this would be written this way:
mkdir /usr/ui/ || cannot_patch
Sorry, but I have got bad news for you — the batch code you want to transcript is wrong:
for /f "tokens=*" %%a in ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') do (
if not errorlevel 1 goto :cannot_patch
)
This is not going to do what you probably think it does. The following steps actually happen:
It executes the command %adb% shell mkdir /usr/ui/ and pipes (|) its output into the command find /i "File exists", but all this happens in a new cmd.exe instance created by the for /F loop.
The for /F loop, well, loops over the lines of the output of the aforesaid commands after they have completed; the new cmd.exe instance is destroyed before starting to iterate.
BUT: The ErrorLevel value resulting from the %adb% …|find … command line is not available in the body of the for /F loop, because this executes in the cmd.exe instance the batch script itself runs in too. This means, that the condition if not errorlevel 1 in the loop body actually reacts on a preceding command, unintentionally.
Since, as I believe, you simply want to test whether the output of %adb% shell mkdir /usr/ui/ contains the sub-string File exists in a case-insensitive manner, you actually just need this:
rem /* Just using `echo` for demonstration purposes, to be replaced;
rem moreover, you may also omit the `&&` or the `||` part: */
%adb% shell mkdir /usr/ui/ | find /i "File exists" && (
echo ErrorLevel 0: sub-string found.
) || (
echo ErrorLevel 1: sub-string NOT found!
)
Unfortunately, I cannot tell you exactly how to translate this, but find might probably have to be substituted by grep, and the operators && and || could be replaced by checking the exit code returned in $?, unless there are similar operators…
Ok, I've installed Dropbox but it didn't corresponded to what I was looking for so I uninstalled it with Revo Pro.
But, when i open the taskmanager there are still processes related to it running in my computer so I decided to make a batch to look out and delete all files that are related to it.
#echo off
cd c:\
:a
set /p a=Phrase that might be realted to it
for /r %%d IN (*.*) DO (
(
findstr /i /m /c:%a% "%%d"
if "%errorlevel%"=="0" del "%%d"
echo %errorlevel%
)
)
pause
The problem is: when I run findstr using loop even when there is no match for my variable "%a%" in an analized file %errorlevel% returns as 0. But when I use findstr alone and there isn't a match %ERRORLEVEL% returns as 1 and 0 for a match.
If I use it, I'll delete all my PC files haha. What's wrong with the code?
Within a parenthesised series of statements, any %var% is replaced by the value of that variable at the time the verb controlling that statement-sequence (or block) is encountered.
Here, the block is the entire sequence of statements controlled by the for. %errorlevel% is replaced by the status of errorlevel at the time the for is encountered, so probably 0.
If you use
findstr /i /m /c:%a% "%%d"
if not errorlevel 1 del "%%d"
echo %errorlevel%
then the run-time value of errorlevel is used (ie. as it changes through the operation of the loop) and the command means "if errorlevel is not (1 or greater than 1) do this..."
The findstr will set errorlevel to 0 on found, 1 on not found and 2 for file not found(IIRC) so NOT (1 or greater than 1) selects 0 only. Note that in certain esoteric circumstances, errorlevel may become negative, but after a findstr I believe 0..2 is the allowed range.
Not sure what's wrong with the code, but you can probably skip it using the && operand.
findstr /i /m /c:%a% "%%d" && del "%%d" echo %errorlevel%
Thanks to Stephan for correcting the example.
Whenever Windows command interpreter encounters ( being interpreted as begin of a command block, it parses the entire command block up to matching ) marking end of the command block and replaces all %variable% by current value of the variable.
This means in this case that before command FOR is the first time executed, everything from ( after DO up to last ) is processed already with replacing all %variable% references by current value of the appropriate variable. Then the already preprocessed block is executed one (on command IF) or more times (on command FOR).
This behavior can be seen by debugging the batch file. For debugging a batch file first #echo off must be removed or commented out with command REM or changed to #echo on. Then a command prompt window must be opened and the batch file is executed from within this command prompt window by typing its name with full path enclosed in double quotes if path or name contains a space character. The Windows command interpreter shows now all command lines and command blocks after preprocessing before executing and of course the standard messages and the error messages output by the commands or by Windows command interpreter itself in case of a syntax error in batch file.
Opening a command prompt window means running cmd.exe with option /K to Keep window open after execution of a command or a batch script. Double clicking on a batch file starts also cmd.exe for processing the batch file, but with parameter /C to Close the window automatically after batch processing terminated independent on cause - successful finished or an error occurred.
The command prompt window opened before running the batch file remains open after batch processing finished successfully or with an error except the batch file contains command EXIT without parameter /B. So experts in batch code writing test batch files always by running them from within a command prompt window instead of double clicking on them.
Delayed variable expansion is needed for variables set or modified and referenced within same command block as explained by help of command SET output on running in a command prompt window set /?.
#echo off
setlocal EnableDelayedExpansion
cd /D C:\
:a
set /P "a=Phrase that might be realted to it: "
for /r %%d in (*) do (
%SystemRoot%\System32\findstr.exe /i /m /c:"%a%" "%%d"
if "!errorlevel!" == "0" del "%%d" >nul
)
endlocal
But for checking the exit code of a previous command there is also if errorlevel syntax as explained by Microsoft in support article Testing for a Specific Error Level in Batch Files.
#echo off
setlocal EnableDelayedExpansion
cd /D C:\
:a
set /P "a=Phrase that might be realted to it: "
for /r %%d in (*) do (
%SystemRoot%\System32\findstr.exe /i /m /c:"%a%" "%%d" >nul
if not errorlevel 1 del "%%d" >nul
)
endlocal
if errorlevel X tests if exit code of previous command or application when it modifies the errorlevel variable at all is greater or equal X. By using if not errorlevel X the check is if last exit code is lower than X which is here a test if exit code is 0.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
cd /?
del /?
echo /?
for /?
if /?
set /?
And see also
Microsoft's command-line reference
SS64.com - A-Z index of the Windows CMD command line
Microsoft article about Using command redirection operators
Answer on question Single line with multiple commands using Windows batch file
How to set environment variables with spaces?
I'm using REN to find files with a certain naming pattern and modify them, like so:
REN "?%var1%?%var2%.S16" "?%var1%?%var3%.S16"
This finds all files like aXaY.S16, bXaY.S16, cXbY.S16 (etc) and renames them to aXaZ.S16, bXaZ.S16, cXbZ.S16 (etc). If it finds what it's looking for, it works just fine. But there's a problem: REN won't halt the operation if it encounters an error.
To prove this is the case, my script is as follows:
#echo off
set /p var1=Enter first var:
set /p var2=Enter second var:
set /p var3=Change second var to:
echo Searching for all files matching ?%var1%?%var2%.S16
REN "?%var1%?%var2%.S16" "?%var1%?%var3%.S16"
echo Errorlevel: %errorlevel%
IF ERRORLEVEL 1 goto :FAIL
echo Success!
PAUSE
goto :eof
:FAIL
echo I done goofed!
PAUSE
exit
I ran this in a folder containing a few hundred files. I searched for files matching ?0?a.S16 (of which there are ~40 results) and asked it to change the 'a' to a 'c', knowing that files with this name already exist and should create a conflict.
Here is the console output (shortened for brevity):
Enter first var: 0
Enter second var:a
Change second var to:c
Searching for all files matching ?0?a.S16
A duplicate file name exists, or the file
cannot be found.
A duplicate file name exists, or the file
cannot be found.
A duplicate file name exists, or the file
cannot be found.
...(etc)...
Errorlevel: 1
I done goofed!
Press any key to continue . . .
The complaint about duplicates goes on for about 40 lines, as expected. As you can see, though, Errorlevel returns 1 at the end of the process instead of quitting at the first sign of trouble, which is what I'd rather it do.
I did consider passing this through FOR /f but I don't know how I would also pass the searchmask through it. I know FOR supports '*' wildcards, but as far as I'm aware, it doesn't support '?' the same way REN does. A possible alternative would be to use regular expressions somehow, but I can't wrap my head around them at all despite trying.
Any clues? Many thanks for taking a look.
Yes, you have documented how the REN command works - it continues to completion, even after a rename fails, and then reports ERRORLEVEL 1 if at least one rename failed.
If you want to halt processing upon the first error, then you will have to write your own loop to rename each file, one at a time. You should not use the simple FOR loop because it can begin iterating before it has scanned the entire directory, so you run the risk of renaming the same file twice. The safe thing to do is use FOR /F coupled with DIR /B /A-D instead.
#echo off
set /p var1=Enter first var:
set /p var2=Enter second var:
set /p var3=Change second var to:
echo Searching for all files matching ?%var1%?%var2%.S16
for /f "eol=: delims=" %%F in (
'dir /b /a-d "?%var1%?%var2%.S16"'
) do ren "%%F" "?%var1%?%var3%.S16" || goto :break
:break
echo Errorlevel: %errorlevel%
IF ERRORLEVEL 1 goto :FAIL
echo Success!
PAUSE
goto :eof
:FAIL
echo Errorlevel: %errorlevel%
echo I done goofed!
PAUSE
exit
Note - The wildcard rules used by REN are not at all intuitive. You should have a look at How does the Windows RENAME command interpret wildcards? to make sure you are getting the results you expect.
replace you ren command with
FOR /f "delims=" %%x IN ('dir /b /a-d "%sourcedir%\?%var1%?%var2%.S16" 2^>nul') DO REN "%sourcedir%\%%x" "?%var1%?%var3%.S16"&IF ERRORLEVEL 1 ECHO failed AT %%x&goto :EOF
note This is a direct patch of my test, where I set sourcedir to a testing directory. In your case, you'd need to make appropriate adjustments.
This makes a directory list in basic form without directories and each filename is then assigned to %%x Then rename is then attempted and the resultant errorlevel interpreted. On fail, show the name for good measure and bail out.
[edit] 2^>nul added into dir command.
This will cause dir errors (like "file not found") to be directed to nowhere. The caret (^) is required to tell cmd that the > is part of the dir command, not the for.