Exit in For loop - Windows Command Processor (CMD.EXE) - command-line

I am trying to find way to break / exit from FOR loop, if there are any error occured.
Below is content of batch file.
#echo on
set myfile=D:\sample.txt
FOR /F "tokens=1,2 delims=," %%i in (%myfile%) do call :process "%%i"
:process
set recfile=%1%
echo %recfile%
echo "Step in Test1"
echo %errorlevel%
pause;
exit /B 0
If %errorlevel% NEQ 0 goto :fail1
:fail1
echo "Step in fail1"
pause;
exit /B 9993
:EOF
Sample.txt has multiple records. If there are any error occured then I am expecting to exit the batch file rather then checking the complete sample.txt file. e.g. on statement
echo %recfile%, If I place some wrong command ech %recfile% which is incorrect command then I am expecting that it should go to fail1 level and exit. It's catured the error code successfully and going to fail1 level however after this statment, it's checking the sample.txt file (next record) again. Is there any way, If I can break / exit FOR loop.
Please advice.
Thanks,

Joey's answer is great. I have used it with success. I discovered that you don't have to exit the script though. You can use goto :SomeLabel, where :SomeLabel is a label outside of the loop.
FOR /F "tokens=1,2 delims=," %%i in (%myfile%) do (
if defined exit goto :ParseError
call :process "%%i"
)
#echo SUCCESS: %myfile%
goto :RestOfScript
:ParseError
#echo FAILURE: cannot parse %myfile%
#echo Using defaults...
:RestOfScript
...

You can set a variable, meaning that the complete loop should be aborted and use it like this:
:fail1
echo "Step in fail1"
pause
set exit=1
And you'd change the loop like this:
FOR /F "tokens=1,2 delims=," %%i in (%myfile%) do (
if defined exit (
exit /b 9993
) else (
call :process "%%i"
)
)
(broken into multiple lines for readability).
Since you are just calling a subroutine from the for loop there is no way for this subroutine to exit the loop directly. Hence the workaround with a variable.

You don't need to call a label
set USBDRIVE=SETLOCAL
set exit=ENABLEDELAYEDEXPANSION
FOR %%D IN (C D E F G H I J K L M N O P Q R S T U V W X Y Z) DO (
DIR %%D:\SOURCES\INSTALL.WIM > nul 2>&1 && call set USBDRIVE=%%D: && call set exit=1
if defined exit goto :dd3
)
:dd3

Related

Is it possible for a batch script to 'catch' a powershell error?

This is my batch code and it's works find as long as the url being checked is a valid url, but gets the following error if you put in an invalid url
URL Status=
( was unexpected at this time.
#Echo Off
Set CheckUrl=`Powershell.exe -nologo -NoProfile -command "(Invoke-WebRequest -Uri http://exceedtstab.infarmbureau.com/Exceed).StatusCode"`
For /f "usebackqdelims=" %%A in (
%CheckUrl%
) Do Set UrlStatus=%%A
Echo URL Status=%UrlStatus%
IF %UrlStatus% == 200 (
Echo URL Connected to internet
goto :eof
) else (
Echo URL Not connected to internet
)
Take a look here: How to fetch Response Code of http post request using curl invoked from a batch script? fetch-response-code-of-http-post-request-using-curl-invoked-from-a-batch
So for your case it should be someting like this:
#echo off
SET URL="http://exceedtstab.infarmbureau.com/Exceed"
SET HTTP=
for /f %%a in ( 'curl -i -w "%%{http_code}" "%URL%"' ) do set HTTP=%%a
if "%HTTP%" == "200" (
echo HTTP Code is: 200
exit /b 0
) else (
echo HTTP Code is: %HTTP%
exit /b 1
)
Or you can test the "UrlStatus" variable with double quotes around it, just in case it's blank; then you won't get that error message, "( was unexpected at this time."
IF "%UrlStatus%" == "200" (

Batch to remove only duplicate segments from strings

There is a fast script or command in batch/powershell to analyse only the duplicate and variable segments in all lines of a txt file and remove them? Example:
input file1.txt:
abcde11234452232131
abcde6176413190830
abcde6278647822786
abcde676122249819113
output file1.txt:
11234452232131
6176413190830
6278647822786
676122249819113
input file2.txt:
11234452232131xyz
6176413190830xyz
6278647822786xyz
676122249819113xyz
output file2.txt:
11234452232131
6176413190830
6278647822786
676122249819113
My script:
#echo off & setlocal enabledelayedexpansion
:startline
set /p first=<#SHA1.txt
set status=notequal
for /f "delims=" %%a in (#SHA1.txt) do (
set second=%%a
if "!first:~0,1!"=="!second:~0,1!" (set status=equal) else (set status=notequal & goto break)
)
if "!status!"=="equal" (
for /f "delims=" %%a in (#SHA1.txt) do (
set second=%%a
echo !second:~1!>>#SHA1.tmp
)
if exist #SHA1.tmp (del #SHA1.txt & ren #SHA1.tmp #SHA1.txt)
goto startline
)
:break
:endline
set /p first=<#SHA1.txt
set status=notequal
for /f "delims=" %%a in (#SHA1.txt) do (
set second=%%a
if "!first:~-1!"=="!second:~-1!" (set status=equal) else (set status=notequal & goto break)
)
if "!status!"=="equal" (
for /f "delims=" %%a in (#SHA1.txt) do (
set second=%%a
echo !second:~0,-1!>>#SHA1.tmp
)
if exist #SHA1.tmp (del #SHA1.txt & ren #SHA1.tmp #SHA1.txt)
goto endline
)
:break
exit
I think that this script is slow to run in multiple files.
What about this (see the explanatory :: comment):
#echo off
::This script assumes that the lines of the input file (provided as command line argument)
::do not contain any of the characters `^`, `!`, and `"`. The lines may be of different
::lengths, empty lines are ignored though.
::The script processes the input file in two phase:
::1. let us call this the analysis phase, which consists of the following steps:
:: * read the first line of the file, store the string and determine its length;
:: * read the second line, walk through all characters beginning from the left and from
:: the right side within the same loop, find the character indexes that point to the
:: first left-most and the last right-most character that do not equal the respective
:: ones in the string from the first line, and store the retreived indexes;
:: * read the remaining lines, and for each one, extract the prefix and the suffix that
:: is indicated by the respective stored indexes and compare them with the respective
:: prefix and suffix from the first line; if both are equal, exit with the loop here
:: and continue with the next line; otherwise, walk through all characters beginning
:: before the previous left-most and after the previous right-most character indexes
:: towards the respective ends of the string, find the character indexes that again
:: point to the first left-most and the last right-most character that do not equal
:: the respective ones in the string from the first line, and update the previously
:: stored indexes accordingly;
::2. let us call this the execution phase, which reads the input file again, extracts the
:: portion of each line that is indicated by the two computed indexes and returns it;
::The output is displayed in the console; to write it to a file, use redirection (`>`).
setlocal EnableDelayedExpansion
set "MIN=" & set "MAX=" & set /A "ROW=0"
for /F usebackq^ delims^=^ eol^= %%L in ("%~1") do (
set /A "ROW+=1" & set "STR=%%L"
if !ROW! equ 1 (
call :LENGTH LEN "%%L"
set "SAV=%%L"
) else if !ROW! equ 2 (
set /A "IDX=LEN-1"
for /L %%I in (0,1,!IDX!) do (
if not defined MIN (
if not "!STR:~%%I,1!"=="!SAV:~%%I,1!" set /A "MIN=%%I"
)
if not defined MAX (
set /A "IDX=%%I+1"
for %%J in (!IDX!) do (
if not "!STR:~-%%J,1!"=="!SAV:~-%%J,1!" set /A "MAX=1-%%J"
)
)
)
if not defined MIN set /A "MIN=LEN, MAX=-LEN"
) else (
set "NXT=#"
if !MIN! gtr 0 for %%I in (!MIN!) do if not "!STR:~,%%I!"=="!SAV:~,%%I!" set "NXT="
if !MAX! lss 0 for %%J in (!MAX!) do if not "!STR:~%%J!"=="!SAV:~%%J!" set "NXT="
if not defined NXT (
if !MAX! lss -!MIN! (set /A "IDX=1-MAX") else (set /A "IDX=MIN-1")
for /L %%I in (!IDX!,-1,0) do (
if %%I lss !MIN! (
if not "!STR:~%%I,1!"=="!SAV:~%%I,1!" set /A "MIN=%%I"
)
if -%%I geq !MAX! (
set /A "IDX=%%I+1"
for %%J in (!IDX!) do (
if not "!STR:~-%%J,1!"=="!SAV:~-%%J,1!" set /A "MAX=1-%%J"
)
)
)
)
)
)
if defined MAX if !MAX! equ 0 set "MAX=8192"
for /F "tokens=1,2" %%I in ("%MIN% %MAX%") do (
for /F usebackq^ delims^=^ eol^= %%L in ("%~1") do (
set "STR=%%L"
echo(!STR:~%%I,%%J!
)
)
endlocal
exit /B
:LENGTH <rtn_length> <val_string>
::Function to determine the length of a string.
::PARAMETERS:
:: <rtn_length> variable to receive the resulting string length;
:: <val_string> string value to determine the length of;
set "STR=%~2"
setlocal EnableDelayedExpansion
set /A "LEN=1"
if defined STR (
for %%C in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if not "!STR:~%%C!"=="" set /A "LEN+=%%C" & set "STR=!STR:~%%C!"
)
) else set /A "LEN=0"
endlocal & set "%~1=%LEN%"
exit /B
This could maybe be improved further, depending also on the data:
if the length of the first line is fixed, or the line lengths vary in a quite small range, you could avoid the :LENGTH sub-routine call and use a constant value instead; if there is a known maximum length of the common prefix/suffix, the line length is even not needed at all;
instead of reading the file twice (due to the two-pass algorithm), you could read it into memory at the beginning and use these data later; for huge files this might be a bad idea though;
I used several for /L loops to walk through certan character ranges, whose bodies are skipped by some if conditions due to lack of while loops or something like exit for; I could have left them using goto, but then I needed to put these loops in separate sub-routines to not break the outer loops; anyway, for [/L] loops finish iterating in the background even when broken by goto, although faster than executing the body; so together with the slow call and goto, I doublt that I would have gained much speed; depending on the data, pure goto loops could be more efficient as they can be left without any remaining background processing, but of course they also needed to be placed in their own sub-routines;
Remove common prefix and/or suffix of unknown length from a list of strings
This batch takes a quite simplistic (and probaply inefficient) approach
It reads the first line and iterates with a growing prefix over the first 30 chararcters
uses findstr to match the lines | pipes the result to find to get a line count
if the line count doesn't match the files total lines the prefix got to long and
the batch continues to the next step.
the same procedure is then used for the suffix
finally the lines are truncated (even prefix and suffix at the same time)
Pass the file name to process as argument, otherwise file1.txt is default.
:: Q:\Test\2018\06\29\SO_51093137.cmd
#echo off & setlocal enabledelayedexpansion
Set "File=%~1"
If not defined File Set "File=file1.txt"
Echo Processing %File%
:: get number of lines
for /f %%i in ('Find /V /C "" ^<"%File%"') Do Set Lines=%%i
Echo #Lines is %Lines%
:: get 1st line
Set /P "Line1=" < "%File%"
Echo Line1 is %Line1%
:: Iterate Prefixlength until Prefix doesn't match all lines
For /L %%i in (1,1,30) Do (
For /F %%A in ('
Findstr /B /L "!Line1:~0,%%i!" "%File%" ^|Find /C /V "" '
) Do Set "EQ=%%A"
If %Lines% neq !EQ! (Set /A "PrefixLength=%%i -1" & Goto :Break1)
)
:Break1
Echo PrefixLength is %PrefixLength%
:: Iterate Suffixlength until Suffix doesn't match all lines
For /L %%i in (-1,-1,-30) Do (
For /F %%A in ('
Findstr /E /L "!Line1:~%%i!" "%File%" ^|Find /C /V "" '
) Do Set "EQ=%%A"
If %Lines% neq !EQ! (Set /A "SuffixLength=%%i +1" & Goto :Break2)
)
:Break2
Echo SuffixLength is %SuffixLength%
Echo ============
For /f "usebackqDelims=" %%A in ("%File%") Do (
Set "Line=%%A"
If %SuffixLength%==0 (
Echo=!Line:~%PrefixLength%!
) Else (
Echo=!Line:~%PrefixLength%,%SuffixLength%!
)
)
Sample output:
> SO_51093137.cmd file2.txt
Processing file2.txt
#Lines is 4
Line1 is 11234452232131xyz
PrefixLength is 0
SuffixLength is -3
============
11234452232131
6176413190830
6278647822786
676122249819113
Following is probably overcomplicating things but it pushed my limit so a great learning experience for me.
$file1 = #(
,'abcde11234452232131'
,'abcde6176413190830'
,'abcde6278647822786'
,'abcde676122249819113'
)
function Test-EqualChar
{
param (
[Scriptblock] $Expression,
[Object[]] $Sequence,
[int] $i
)
!(($Sequence[1..($Sequence.Length -1)] | % {(&$Expression $_ $i) -eq ($Sequence[0][$i])}) -contains $False)
}
$OneChar = {param($x, $i) $x[$i]}
$start = for($i=0;$i -lt ($file1 | % {$_.Length} | Measure -Minimum | Select -ExpandProperty Minimum);$i++) {
if (!(Test-EqualChar $OneChar $file1 $i)) {$i; break}
}
$file1 | % {$_.Substring($start, $_.Length-$start)}
I'll leave it as an excercise to work out reversing (or padding) the strings to remove equal characters from the end of strings
This solution uses a different approach. IMHO this is the fastest way to process a file.
#echo off
setlocal EnableDelayedExpansion
if "%~1" equ "" echo Usage: %0 filename & goto :EOF
if not exist "%~1" echo File not found: "%~1" & goto :EOF
rem Read first two lines and get their base 0 lengths
( set /P "line1=" & set /P "line2=" ) < %1
call :StrLen0Var len1=line1
call :StrLen0Var len2=line2
rem Extract the largest *duplicate segment* from first two lines
set "maxDupSegLen=0"
for /L %%i in (0,1,%len1%) do (
for /L %%j in (0,1,%len2%) do (
if "!line1:~%%i,1!" equ "!line2:~%%j,1!" (
rem New duplicate segment, get its length and keep the largest one
set /A "maxLen=len1-%%i+1, maxLen2=len2-%%j+1"
if !maxLen2! gtr !maxLen! set "maxLen=!maxLen2!"
for /L %%l in (1,1,!maxLen!) do (
if "!line1:~%%i,%%l!" equ "!line2:~%%j,%%l!" set "dupSegLen=%%l"
)
if !dupSegLen! geq !maxDupSegLen! (
set /A "maxDupSegLen=dupSegLen, maxDupSegPos=%%i"
)
)
)
)
set "dupSeg=!line1:~%maxDupSegPos%,%maxDupSegLen%!"
rem Process the file removing duplicate segments
for /F "delims=" %%a in (%1) do (
set "line=%%a"
echo !line:%dupSeg%=!
)
goto :EOF
Get the length base 0 of a variable
:StrLen0Var len= var
setlocal EnableDelayedExpansion
set "str=!%2!"
set "len=0"
for /L %%a in (12,-1,0) do (
set /A "newLen=len+(1<<%%a)"
for %%b in (!newLen!) do if "!str:~%%b,1!" neq "" set "len=%%b"
)
endlocal & set "%1=%len%"
input1.txt:
abcde11234452232131
abcde6176413190830
abcde6278647822786
abcde676122249819113
output:
11234452232131
6176413190830
6278647822786
676122249819113
input2.txt:
11234452232131xyz
6176413190830xyz
6278647822786xyz
676122249819113xyz
output:
11234452232131
6176413190830
6278647822786
676122249819113
"The rows have variable length and multiple duplicate parts may occur".
input3.txt:
abcde11234452232131
6176abcde4131908abcde30
6278647abcde822786
676122249819113abcde
output:
11234452232131
6176413190830
6278647822786
676122249819113

CMD Batch file to toggle desktop icon from icon1 to icon2 and vis versa, needs 2 double clicks?

I am writing a batch file that uses a VBScript function to toggle a desktop icon between icon1.ico and icon2.ico. Essentially I would like to show the user the state of the program, whether my app is in mode_On or mode_Off.
The problem is that the shortcut "mode" on the desktop is doubled clicked, which changes icon to "modeOn", then when double clicking again and it won't execute the batch file, until I double click a second time. Each execution of the script requires 2 x double clicks. Looking to execute/toggle icons in the one double click like all programs.
I have tried to implement ie4uinit.exe -ClearIconCacheand cmd /C ie4uinit.exe -show however the places I tried to put that code did not work.
Here is my BATCH file with VBScript :) I have
#ECHO
mode con lines=30 & color F2
IF EXIST a.txt (echo a.txt exists) ELSE (
echo Cannot find file a.txt. Visit http://www.example.com/issues and look up Issue#1.
pause
exit
)
FOR /F "tokens=1 delims=," %%G IN (a.txt) DO #set xx=%%G
echo %xx%
IF %xx%==a (
echo a.txt reports zero therefore current config is Currently modeOff
rename "mode" "mode2"
rename "mode1" "mode"
echo mode was renamed to mode2 and mode1 was renamed to mode
#echo off
echo b > a.txt
Call:CreateShortcut "Desktop\modeshortcut" "mode" "Winver.exe,0"
pause
exit
)
IF %xx%==b (
echo a.txt reports zero therefore current config is Currently modeOn
rename "mode" "mode1"
rename "mode2" "mode"
echo mode was renamed to mode1 and mode2 was renamed to mode
#echo off
echo a > a.txt
Call:CreateShortcut "Desktop\modeshortcut" "mode" "%Windir%\System32\moricons.dll,6"
pause
exit
)
:: ======== FN ======
GOTO :EOF
:: /// check if the app has failed
::****************************************************************************************************
:CreateShortcut -c <ApplicationPath> <ShortcutName> <Icon>
(
echo Call Shortcut ("%~1","%~2","%~3"^)
echo ^'**********************************************************************************************^)
echo Sub Shortcut(ApplicationPath,Name,Icon^)
echo Dim objShell,DesktopPath,objShortCut,MyTab
echo Set objShell = CreateObject("WScript.Shell"^)
echo MyTab = Split^(ApplicationPath,"\"^)
echo If Name = "" Then
echo Name = MyTab(UBound^(MyTab^)^)
echo End if
echo DesktopPath = objShell.SpecialFolders("Desktop"^)
echo Set objShortCut = objShell.CreateShortcut(DesktopPath ^& "\" ^& Name ^& ".lnk"^)
echo objShortCut.TargetPath = Dblquote^(ApplicationPath^)
echo ObjShortCut.IconLocation = Icon
echo objShortCut.Save
echo End Sub
echo ^'**********************************************************************************************
echo ^'Fonction pour ajouter les doubles quotes dans une variable
echo Function DblQuote(Str^)
echo DblQuote = Chr(34^) ^& Str ^& Chr^(34^)
echo End Function
echo ^'**********************************************************************************************
)> Shortcutme.vbs
Start /wait Shortcutme.vbs
Del Shortcutme.vbs
::cmd /C ie4uinit.exe -show
::cmd ie4uinit.exe -ClearIconCache
Exit
::****************************************************************************************************

Perl Script return value in DOS batch file

I have a DOS batch file that calls a Perl Script file:
ssutexec.pl prog=MyProg I_CARD=MyCard O_F071=ME007 F70P=MYF70P TRIG=MYTRIG
set status=%errorlevel%
if NOT %status% == 0 (
echo *******************************************************
echo MPSBM070 JOB stopped with a RETURN CODE of %status%
echo ************** END OF JOB *****************************
goto JobEnd
)
Inside the ssutexec.pl perl script file, an error condition is executed and the script exits with exit($status):
if ($status != 0)
{
system("ssutdttm.pl SSUTEXEC \"finished processing $prog_nam (Abnormal End) on\"") ;
print "******************************************************************" ;
print " JOB STOPPED - Due to Return code of ($status)" ;
print "*****************END OF JOB***************************************" ;
system("ssutdttm.pl SSUTEXEC \"finished processing $prog_nam (Abnormal End) on\"") ;
system("ssuttmdr.pl -c -b $starttime -m \"$prog_nam\"") ;
exit($status) ;
}
I am not a perl script guy. Errorlevel isn't being triggered so I am not getting the error. How can I trap the value of the perl script exit code within my DOS batch file?
INDLUDING EDITS:
The new batch file looks like this:
#echo off
setlocal enabledelayedexpansion
if not defined run_env goto ScriptExit
echo ******************************************************************
echo * MYPROG *
echo ******************************************************************
perl ssutdttm.pl MYPROG Start -
perl ssutexec.pl prog=MyProg I_CARD=MyCard O_F071=ME007 F70P=MYF70P TRIG=MYTRIG
set status=!errorlevel!
if NOT %status% == 0 (
echo *******************************************************
echo MYPROG JOB stopped with a RETURN CODE of %status%
echo ************** END OF JOB *****************************
goto JobEnd
)
:JobEnd
if %status% == 0 (
echo *******************************************************
echo NORMAL END OF JOB - RETURN CODE of %status%
echo ******** NORMAL END OF JOB ****************************
) ELSE (
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
echo ABNORMAL END OF JOB - RETURN CODE of %status%
echo !!!!!! ABNORMAL END OF JOB !!!!!!!!!!!!!!!!!!!!!!!!!!!!
)
ssutdttm.pl MYPROG end -
exit /b %status%
::
:ScriptExit
echo run_env: %run_env%
Working with batch files can really be a pain (DOS batch files make bash scripts look wonderful, and bash stinks!). The problem could be cropping up from the way batch files handle variable expansion. Here's something to try:
Add the following line to the top of your batch script (I usually put it at the top, just under my #echo off line): setlocal enabledelayedexpansion
Change all %ERRORLEVEL% references to !ERRORLEVEL! instead (exclamation marks instead of percent signs).
These changes will have the effect of causing variables to get expanded at execution time, rather than at parse-time. I've seen the %ERRORLEVEL% value get mangled when not using the delayed expansion option. Read this EnableDelayedExpansion information page for more.
I think you should be calling perl.exe with the script as an argument:
perl ssutexec.pl prog=MyProg ...
In my tests, if I have just the script name, I get prompted for what I want to use to open the test.pl file. That means perl.exe is not executing my Perl code.
Your file associations could likely be there, but your script could still be failing because of the nuances of Windows.
I tested with my two very simple scripts:
test.bat
#ECHO OFF
perl test.pl
set status=%errorlevel%
if NOT %status% == 0 (
echo status was set with value of %status%
)
and test.pl
use warnings;
use strict;
my $status = 44;
exit($status)
and the expected output:
C:\Dropbox\Programming\Perl\Learn\blah>test
status was set with value of 44

if conditional based on output in batch?

I'd like to redirect my program to a certain set of commands if the cmd window outputs: "Can't find ref_eng...", how can i accomplish this? In the codes below, at the 2nd line containing the For, is the location where the cmd.exe can output "can't find ref_eng" if !refLogPath! doesnt exist. At this point, I'd like to redirect my program elsewhere...
<!logPath! (
For /F "tokens=*" %%R in (!refLogPath!) DO (
if %ERRORLEVEL% NEQ 0 (
ECHO Check certain lines of code
)
set logLine=
set /p logLine=
set refLogLine=%%R
REM Check line by line of log against refLog
REM assume ALL times have been replaced with: "xx:xx:xx"
REM if corresponding lines mismatch
if NOT "!logLine!"=="!refLogLine!" (
Echo.
Echo line below is Incorrect:
set lnCorrect=false
REM output to command line: can be put into .log/.txt later
REM output ANY and ALL incorrect line in log file
ECHO !logLine!
)
)
)
Check refLogPath before the for loop.
Eg:
if !refLogPath!xx == xx (
REM refLogPath is empty or unset
goto :SomewhereElse
) else (
if not exist !refLogPath! (
REM refLogPath is missing
goto :SomewhereElse
)
)
For /F "tokens=*" %%R in (!refLogPath!) DO (
...etc...