Batch Script - Skip on errorlevel in a For loop - batch-file

I am trying to have this script go to the next part if the error level of a ping to a computer does not equal 0, but I cannot get it to work. The output says the syntax is not correct. Thank you.
#echo
setlocal EnableDelayedExpansion
for /f %%a in (main.txt) do (
ping -n 1 %%a > NUL
IF %ERRORLEVEL%==0 (GOTO :COPY) ELSE GOTO :SKIP
:COPY
ROBOCOPY C:\Blah C:\Bloh
ECHO FILE COPIED
:SKIP
ECHO FILE NOT COPIED
)

You should try this:
#echo
setlocal EnableDelayedExpansion
for /f %%a in (main.txt) do (
ping -n 1 %%a >NUL
IF "!ERRORLEVEL!"=="0" (
ROBOCOPY "C:\Blah" "C:\Bloh"
ECHO FILE COPIED
) ELSE (
ECHO FILE NOT COPIED
)
)
PAUSE
There are a couple of things wrong with your code. First of all, you enable Delayed Expansion, but don't actually use it, only variables inside ! get expanded delayed. I also put quotes around your filepaths, to protect them against paths with spaces and stuff. Finally, goto and labels don't work inside for loops, so you need to replace them with if else logic

goto :Label inside a parenthesised block of code like for loops breaks the block/loop context, so the code at the label is executed as if it were outside of the block/loop. Therefore you need to work around that.
Dennis van Gils points out a way how to do it -- using if/else logic (his method as well as the following slightly modified snippet (applying numeric comparison) both require delayed expansion):
setlocal EnableDelayedExpansion
for "usebackq" /F %%A in ("main.txt") do (
> nul ping -n 1 %%A
if !ErrorLevel! EQU 0 (
robocopy "C:\Blah" "C:\Bloh"
echo FILE COPIED
) else (
echo FILE NOT COPIED
)
)
endlocal
Or like this, avoiding the necessity of delayed expansion:
for "usebackq" /F %%A in ("main.txt") do (
> nul ping -n 1 %%A
if ErrorLevel 1 (
echo FILE NOT COPIED
) else (
robocopy "C:\Blah" "C:\Bloh"
echo FILE COPIED
)
)
To check the ErrorLevel against (non-)zero, you can also use the && and || operators:
for "usebackq" /F %%A in ("main.txt") do (
> nul ping -n 1 %%A || (
echo FILE NOT COPIED
) && (
robocopy "C:\Blah" "C:\Bloh"
echo FILE COPIED
)
)
Finally, if you do want to keep the goto :Label structure, you need to use a sub-routine in order to move this part of the code outside of the () block (you also do not need delayed expansion here):
for "usebackq" /F %%A in ("main.txt") do (
> nul ping -n 1 %%A
call :SUB "C:\Blah" "C:\Bloh"
)
exit /B
:SUB
if %ErrorLevel% NEQ 0 goto :SKIP
robocopy "%~1" "%~2"
echo FILE COPIED
:SKIP
goto :EOF

Related

How do I substring a dynamic variable value in windows batch scripting

Not able to substring the dynamic variable inside forloop in Windows batch script.
I have the properties file in my git hub in the below format.
"collectionName=TestCollectionRun.json=test"
So I have written the below code to fetch this values.But the requirement is that I need to strip of the '.json' part from collection name.With the below code I am not able to set/echo that value.Can you please help on this!
#ECHO ON
:BEGIN
IF EXIST "test.properties" ECHO Found properties file, reading file..
SET props=test.properties
setlocal EnableDelayedExpansion
For /F "delims== tokens=1,2,3" %%G in (%props%) Do (
if "%%I" EQU "test" if "%%G" EQU "collectionName" SET collName=%%H(
SET finalCollName=%collName%:~0,-5
ECHO %finalCollName%
)
)
:END
We need the ECHO to return "TestCollectionRun".currently its not returning anything.
For /F "delims== tokens=1,2,3" %%G in (%props%) Do (
if "%%I" EQU "test" if "%%G" EQU "collectionName" SET "collName=%%~nH"&echo %%~nH
)
ECHO %CollName%
Note the second ) is now redundant. Your problem has to do with delayedexpansion which you are invoking but not using. call %%collname%% within the for loop would have shown the value after assignment if required.
This code works by interpreting %%H as a filename and assigning simply the name part of %%H (%%~nH)
Given a line content of collectionName=TestCollectionRun.json=test, here's a quick rewrite of what I think you're tring to do:
#Echo Off
Set "props=test.properties"
If Not Exist "%props%" (
Echo Properties file not found!
Echo Exiting..
Timeout /T 3 /NoBreak >NUL
Exit /B
)
Echo Found properties file, reading file..
For /F "UseBackQ Tokens=1-3 Delims==" %%A In ("%props%") Do (
If /I "%%C" == "test" If /I "%%A" == "collectionName" Echo %%~nB
)
Pause
If you wanted to do something with the collection name within the loop then you would probably need to use delayed expansion:
#Echo Off
SetLocal DisableDelayedExpansion
Set "props=test.properties"
If Not Exist "%props%" (
Echo Properties file not found!
Echo Exiting..
Timeout /T 3 /NoBreak >NUL
Exit /B
)
Echo Found properties file, reading file..
For /F "UseBackQ Tokens=1-3 Delims==" %%A In ("%props%") Do (
If /I "%%C" == "test" If /I "%%A" == "collectionName" (
Set "collName=%%B"
SetLocalEnableDelayedExpansion
Echo !collName!
Rem Perform substring task on the variable named collName
Set "finalCollName=!collName%:~0,-5!"
Echo !finalCollName!
EndLocal
)
)
Pause
Note, these answers will not work, as is, if your string is surrounded by doublequotes, (as in your question body), or if the line content differs (e.g. begins with spaces or tabs).
[Edit /]
Looking at your 'after the fact' question in the comments, it is clear that you do not need to substring the variable at all, so should use the first method posted:
Echo Found properties file, reading file..
For /F "UseBackQ Tokens=1-3 Delims==" %%A In ("%props%") Do (
If /I "%%C" == "test" If /I "%%A" == "collectionName" (
newman run "%%B" -e "%envName%" --insecure --reporters cli,htmlextra --reporter-htmlextra-export "newman\%BUILD_NUMBER%\%%~nB.html" --disable-unicode
)
)
Pause
This assumes that both %envName% and %BUILD_NUMBER% have been previously defined correctly.

check for spaces in file name inside of for loop in batch file

I have a batch script that is calling a VBscript file. It reiterates through all files in a watched folder.
It needs to verify if the file name has spaces in it and if so reject the file and not process it with the VBS.
I must have an error on the code as I get the error message:
ELSE was not expected at this time.
I have looked at the documentation and searched for the answer for quite some time, including this question: check "IF" condition inside FOR loop (batch/cmd)
But still, I can't see what is wrong in my syntax:
#ECHO OFF
setlocal ENABLEDELAYEDEXPANSION
call :ReadIni Infolder inFolder
call :ReadIni Outfolder outFolder
echo %inFolder%
echo %outFolder%
pause
:StartLoop
FOR %%I in (%inFolder%\*.srt) DO (
ECHO %%I
ECHO %%~nxI
SET TESTNAME=%%~nxI
ECHO !TESTNAME!
ECHO !TESTNAME: =_!
PAUSE
IF NOT !TESTNAME!==!TESTNAME: =_! (
move "%~sdp0%%~nxI" "%outFolder%\ERROR_IN_FILE_NAME_%%~nxI"
) ELSE (
copy /y "%%I" "%~sdp0%%~nxI"
%~sdp0SRToffset.vbs "%~sdp0%%~nxI" "%~sdp0%%~nxI"
IF %ERRORLEVEL%==1 (
Goto StartLoop
) else (
move "%~sdp0%%~nxI" "%outFolder%\"
move "%~sdp0QC_%%~nxI" "%outFolder%\"
del "%%I"
)
)
)
timeout /t 1
goto StartLoop
:ReadIni
FOR /F "tokens=2 delims==" %%a in ('find "%~1=" config.ini') do set %~2=%%a
exit /b
Any help would be appreciated.
IF NOT "!TESTNAME!"=="!TESTNAME: =_!" (
...
IF %ERRORLEVEL%==1 (
Quoting the strings causes cmd to regard the string as a single entity.
Note that the following if %errorlevel% will be executed using the value of errorlevel at :startloop. (See delayed expansion for reasoning.)
Cure by using if !errorlevel!==1 (. (Using the runtime value of errorlevel as established by the vbs routine.)

Windows Batch FOR Loop improvement

I have a batch to check the duplicate line in TXT file (over one million line) with 13MB, that will be running over 2hr...how can I speed up that? Thank you!!
TXT file
11
22
33
44
.
.
.
44 (over one million line)
Existing Batch
setlocal
set var1=*
sort original.txt>sort.txt
for /f %%a in ('type sort.txt') do (call :run %%a)
goto :end
:run
if %1==%var1% echo %1>>duplicate.txt
set var1=%1
goto :eof
:end
This should be the fastest method using a Batch file:
#echo off
setlocal EnableDelayedExpansion
set var1=*
sort original.txt>sort.txt
(for /f %%a in (sort.txt) do (
if "%%a" == "!var1!" (
echo %%a
) else (
set "var1=%%a"
)
)) >duplicate.txt
This method use findstr command as in aschipfl's answer, but in this case each line and its duplicates are removed from the file after being revised by findstr. This method could be faster if the number of duplicates in the file is high; otherwise it will be slower because the high volume data manipulated in each turn. Just a test may confirm this point...
#echo off
setlocal EnableDelayedExpansion
del duplicate.txt 2>NUL
copy /Y original.txt input.txt > NUL
:nextTurn
for %%a in (input.txt) do if %%~Za equ 0 goto end
< input.txt (
set /P "line="
findstr /X /C:"!line!"
find /V "!line!" > output.txt
) >> duplicate.txt
move /Y output.txt input.txt > NUL
goto nextTurn
:end
#echo off
setlocal enabledelayedexpansion
set var1=*
(
for /f %%a in ('sort q42574625.txt') do (
if "%%a"=="!var1!" echo %%a
set "var1=%%a"
)
)>"u:\q42574625_2.txt"
GOTO :EOF
This may be faster - I don't have your file to test against
I used a file named q42574625.txt containing some dummy data for my testing.
It's not clear whether you want only one instance of a duplicate line or not. Your code would produce 5 "duplicate" lines if there were 6 identical lines in the source file.
Here's a version which will report each duplicated line only once:
#echo off
setlocal enabledelayedexpansion
set var1=*
set var2=*
(
for /f %%a in ('sort q42574625.txt') do (
if "%%a"=="!var1!" IF "!var2!" neq "%%a" echo %%a&SET "var2=%%a"
set "var1=%%a"
)
)>"u:\q42574625.txt"
GOTO :EOF
Supposing you provide the text file as the first command line argument, you could try the following:
#echo off
for /F "usebackq delims=" %%L in ("%~1") do (
for /F "delims=" %%K in ('
findstr /X /C:"%%L" "%~1" ^| find /C /V ""
') do (
if %%K GTR 1 echo %%L
)
)
This returns all duplicate lines, but multiple times each, namely as often as each occurs in the file.

Removing values from set in Batch

I'm trying to write a script for work that tells me which machines on a network are online and which are offline.
Currently I have it showing me online/offline status, taking the PC names from a text file as input into a set variable.
The code I have so far is:
#echo off
setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in (VC.txt) do call :append %%a
:ping
for %%i in (%VC) do (
ping %%1 -n 1 >nul
call :test %%i
)
echo.
goto :ping
:test
IF %ERRORLEVEL% EQU 0 (
echo Pinging %1
) else (
echo Off %1
)
goto :eof
:append
if defined VC (
set VC=%VC% %1
) else (
set VC=%1
)
What I want to happen is once a machine comes online, to remove it from the list. Basically just only show the list of the offline machines.
Is this possible without wiping the whole set and creating it fresh?
This mod do what you want:
:test
IF %ERRORLEVEL% EQU 0 (
echo Pinging %1
REM Remove this machine from the list
set "VC=!VC: %1=!"
) else (
echo Off %1
)
goto :eof
However, you have a couple small errors in your code. This is the fixed section:
:ping
for %%i in (%VC%) do (
ping %%i -n 1 >nul
call :test %%i
)
echo.
goto :ping
A couple points unrelated to your problem. If the machine names are single words with no spaces, then the "tokens=*" part in the for command is not needed. Also, all those call's and subroutines just complicate the code. This is the way I would do this:
#echo off
setlocal EnableDelayedExpansion
set "VC="
for /F %%a in (VC.txt) do set "VC=!VC! %%a"
:ping
for %%i in (%VC%) do (
ping %%i -n 1 >nul
IF !ERRORLEVEL! EQU 0 (
echo Pinging %%i
REM Remove this machine from the list
set "VC=!VC: %%i=!"
) else (
echo Off %%i
)
)
echo/
if defined VC goto :ping
echo All machines are on line

Delete duplicate files using Windows command line

Given two directories c:\foo and c:\bar I want to delete the files in c:\bar that are identical to files present in c:\foo. I can use the fc command to compare each file in c:\bar with a file of the same name in c:\foo and delete duplicates manually. Is there a simple way to automate this using CMD?
If identical means similar or alike in every way: in every way, not only in date and size, therefore forced binary comparison:
#ECHO OFF >NUL
SETLOCAL enableextensions
pushd "D:\bat\FooBar"
for /F "delims=" %%G in ('dir /B /A:-D *.*') do (
call :proFC "%%~fG" "D:\bat\FooFoo\%%~nxG"
)
popd
ENDLOCAL
goto :eof
:raiseerror
exit /B %1
:proFC
call :raiseerror 321
fc /B "%~1" "%~2" >NUL 2>&1
if %errorlevel% EQU 0 (
echo del "%~1"
) else (
echo %errorlevel% "%~2"
)
goto :eof
Commented crucial points in the above script:
pushd ... switches current working directory
for /F ... loop treats static file list of the Bar folder
call :proFC ... with properly quoted line arguments parameters
popd switches current working directory back
goto :eof ends the script
:raiseerror subroutine returns exit code via exit /B %1
:proFC productive subroutine
call :raiseerror 321 important as For an invalid switch (with two passed files) an error message is printed but the errorlevel is not changed
fc /B "%~1" "%~2" >NUL 2>&1 output and error messages redirected to NUL as the errorlevel is important;
if %errorlevel% EQU 0 (
echo del "%~1" a file delete merely _echo_ed for debugging purposes
) else (
echo %errorlevel% "%~2" for debugging purposes (see below).
goto :eof returns from the subroutine
FC will set an ErrorLevel as follows (but see a note at the call :raiseerror 321 point):
-1 Invalid syntax (e.g. only one file passed)
0 The files are identical.
1 The files are different.
2 Cannot find at least one of the files.
#echo off
cd c:\bar
for %%a in (*.*) do for %%b in ("c:\foo\%%a") do (
if exist "%%b" (
if "%%~Ta %%~Za" equ "%%~Tb %%~Zb" (
del "%%a"
) else (
fc "%%a" "%%b" > NUL
if not errorlevel 1 del "%%a"
)
)
)
If two files may have different modification dates but be equal, remove the %%~T.. parts in the comparison.

Resources