Displaying skipped batch comments - windows

So I wanted to have plaintext comments in my bat file with useage information at the top by skipping the text using goto but wanted to display the text as help info if say /? switch was used..
I managed to get the text to display with this method
#echo off
goto start
:help
some
text
here not commands
#echo off
goto:eof
:start
echo on && prompt $h && call :help 2>nul
which displays this:
some
text
here not commands
Does anyone know a way to remove the blank lines?

I typically begin lines with :: to indicate a comment, and ::: to indicate documentation. I can then use FOR /F with FINDSTR to easily print out the documentation, as long as no displayed documentation line begins with :.
#echo off
::Documentation
:::
:::some
:::text
:::here not commands
:::
:::
::Show help
call :help
exit /b
:help
for /f "tokens=* delims=:" %%A in ('findstr "^:::" "%~f0"') do #echo(%%A
exit /b
If I have a lot of documentation, then I may put a GOTO :START at the top to improve script start-up time.
#echo off
goto :start
::Documentation
:::
:::some
:::text
:::here not commands
:::
:::
:start
::Show help
call :help
exit /b
:help
for /f "tokens=* delims=:" %%A in ('findstr "^:::" "%~f0"') do #echo(%%A
exit /b

Another one! ;-)
#echo off
goto help-end
:help-start
some
text
here not commands
:help-end
if "%~1" neq "/?" goto exitHelp
set "start="
for /F "delims=:" %%a in ('findstr /N /B ":help" "%~F0"') do (
if not defined start (set "start=%%a") else set "end=%%a"
)
for /F "skip=%start% tokens=1* delims=:" %%a in ('findstr /N "^" "%~F0"') do (
if "%%a" equ "%end%" goto exitHelp
echo(%%b
)
:exitHelp
echo Normal process continue here

#rojo
like this?
#echo off
goto start
:help
call :heredoc help && goto helpend
some
text
here not commands
:helpend
goto:eof
:start
call :help
pause
:heredoc <uniqueIDX>
setlocal enabledelayedexpansion
set go=
for /f "delims=" %%A in ('findstr /n "^" "%~f0"') do (
set "line=%%A" && set "line=!line:*:=!"
if defined go (if #!line:~1!==#!go::=! (goto :EOF) else echo(!line!)
if "!line:~0,13!"=="call :heredoc" (
for /f "tokens=3 delims=>^ " %%i in ("!line!") do (
if #%%i==#%1 (
for /f "tokens=2 delims=&" %%I in ("!line!") do (
for /f "tokens=2" %%x in ("%%I") do set "go=%%x"
)
)
)
)
)
goto :EOF
Output:
some
text
here not commands
Press any key to continue . . .
It definitely works!

Related

Single line nested loop

I am experimenting with a single line cmd /c to get an inner loop without branching. (Actually i have the showLines routine which performs the loop.) I know its worst for performance but i want to know if its possible to get it run without quotes. Currently it raises "%G was unexpected at this time" error. So, it needs some correct escaping or expansion of variables.
#echo off
setlocal enableDelayedExpansion
set "param=%~1"
netstat -aonb | findstr /n $ > tmpFile_Content
for /F "tokens=*" %%A in ('type tmpFile_Content ^| findstr /r /c:"%param%" /i') do (
SET line=%%A
for /F "tokens=1 delims=:" %%I in ("!line!") DO (
set /a LineNum=%%I
rem set /a NextLineNum=LineNum+1
)
set /a lineNum=!LineNum!-1
if !lineNum!==0 ( set param="tokens=*" ) else ( set param="tokens=* skip=!lineNum!" )
rem FOLLOWING WORKS FINE in quotes
cmd /q /v:on /c "#echo off && setlocal enableDelayedExpansion && set cnt=2 && for /F %%param%% %%B in (tmpFile_Content) do ( echo %%B && set /a cnt-=1 >nul && if ^!cnt^!==0 exit /b )"
rem Following does not work even though cmd should take the rest of arguments after /c
cmd /q /v:on /c setlocal enableDelayedExpansion && FOR /F "tokens=*" %%C IN ('echo !param!') DO ( for /F %%C %%G in (tmpFile_Content) do ( echo %%G && set /a cnt-^=1 >nul && if ^!cnt^!==0 exit /b ))
rem call :showLines !LineNum!
)
del tmpFile_Content
goto :eof
:showLines
set /a lineNum=%1-1
set cnt=2
for /F "tokens=* skip=%lineNum%" %%B in (tmpFile_Content) do (
echo %%B
set /a cnt-=1
if !cnt!==0 goto exitLoop
)
:exitLoop
exit /b
to construct for loops with variable parameters, you essentially need to define and execute them as a macro. Eg:
#Echo Off & Setlocal ENABLEdelayedExpasnion
Set param="tokens=* delims="
Set "test=string line"
Set For=For /F %param% %%G in ("^!test^!") Do echo %%G
%For%
Of course you could go even further, and build the entire for loop with another for loop macro on the fly.
UPDATE:
Method for defining conditional concatenation of commands now exampled
Syntax simplified to allow the same usage form for regular expansion and within codeblocks by having the constructor macro call a subroutine to expand the new for loop once it's constructed.
delayed concatenation variable usage simplified to avoid the escaping requirement
#Echo off
::: { Macro Definition
Setlocal DisabledelayedExpansion
(set \n=^^^
%= This creates an escaped Line Feed - DO NOT ALTER =%
)
::: [ For Loop Constructor macro. ] For advanced programmers who need to use dynamic for loop options during code blocks.
::: - usage: %n.For%{For loop options}{variable set}{For Metavariable}{commands to execute}
::: - use delayed !and! variable to construct concatenated commands in the new for loop.
Set n.For=For %%n in (1 2) Do If %%n==2 (%\n%
Set FOR=%\n%
For /F "tokens=1,2,3,4 Delims={}" %%1 in ("!mac.in!") Do (%\n%
Set "FOR=For /F %%1 %%3 in ("!%%2!") Do (%%~4)"%\n%
)%\n%
Call :Exc.For%\n%
)Else Set mac.in=
Set "and.=&&"
Set "and=!and.!"
::: } End macro definition.
Setlocal EnableDelayedExpansion& rem // required to expand n.For constructor macro
::: - Usage examples:
Set "example=is a string line"
%n.For%{"tokens=* delims="}{example}{%%G}{Echo/%%~G}
%n.For%{"tokens=1,2,3,4 delims= "}{example}{%%G}{"Echo/%%~J %%~G %%~H %%~I !and! Echo/%%~I %%~G %%~H %%~J"}
Set "example2=Code block example"
For %%a in (1 2 3) do (
%n.For%{"Tokens=%%a Delims= "}{example2}{%%I}{"For /L %%# in (1 1 4) Do (Set %%I[%%#]=%%a%%#) !and! Set %%I[%%#]"}
)
Pause > Nul
Goto :EOF
:Exc.For
%FOR%
Exit /B
Example output:
is a string line
line is a string
string is a line
Code[1]=11
Code[2]=12
Code[3]=13
Code[4]=14
block[1]=21
block[2]=22
block[3]=23
block[4]=24
example[1]=31
example[2]=32
example[3]=33
example[4]=34
Finally, i came up with following to execute code given in string for anyone interested experimentally and may be for some insight about escaping and expansion.
I use macro instead of cmd which will be much more faster i think(not sure because its said that "call" also causes launch of cmd).
So, it is a simple one-liner without a lot of extra code. But things easily become complicated and when extra escaping and special characters used then #T3RR0R's macro routine would be a necessity.
#echo off
setlocal EnableDelayedExpansion
set "param=%~1"
netstat -aonb | findstr /n $ > tmpFile_Content
for /F "tokens=*" %%A in ('type tmpFile_Content ^| findstr /r /c:"%param%" /i') do (
SET line=%%A
for /F "tokens=1 delims=:" %%I in ("!line!") DO (
set /a LineNum=%%I
rem set /a NextLineNum=LineNum+1
)
set /a lineNum=!LineNum!-1
rem CORRECT QUOTING
if !lineNum!==0 ( set "param="tokens=*"" ) else ( set "param="tokens=* skip=!lineNum!"" )
rem FOLLOWING WORKS FINE in quotes
rem cmd /q /v:on /c "set cnt=2 && for /F ^!param^! %%B in (tmpFile_Content) do ( echo %%B && set /a cnt-=1 >nul && if ^!cnt^!==0 exit /b )"
rem For reading !cnt! use !x!cnt!x!.
rem Only one extra variable used, and in routine its replaced with !(exclamation) for our "cnt" variable.
set "x=^!x^!"
call :ExecCode "set cnt=2 && for /F ^!param^! %%%%B in (tmpFile_Content) do (echo %%%%B && set /a cnt=!x!cnt!x!-1 >nul && if !x!cnt!x!==0 (exit /b) )"
rem call :showLines !LineNum!
)
del tmpFile_Content
goto :eof
:ExecCode
setlocal
rem Replace with exclamation for variable
set "x=^!"
set "s=%~1"
%s%
endlocal
exit /b
:showLines
set /a lineNum=%1-1
set cnt=2
for /F "tokens=* skip=%lineNum%" %%B in (tmpFile_Content) do (
echo %%B
set /a cnt-=1
if !cnt!==0 goto exitLoop
)
:exitLoop
exit /b

How to remove names from a list in batch

I'm using windows bath, I have a list of names that I can add to but I don't know how to remove a name from the list.
So far my code is:
#echo off
setlocal enabledelayedexpansion
set allchoices=123456789
set "names=Bob,Steven,Harry"
set amount=6 ::max limit of list
set list=0
:start
::echoes a list of all names in the list
for /l %%i in (1; 1; %amount%) do (
call :sub %%i
)
goto check
:sub
for /f "tokens=%1 delims=," %%a in ("%names%") do (
echo %%i. %%a
set /a list=list+1
)
goto :eof
:check
::Remove a name from the list
choice /c !allchoices:~0,%list%! /m "What name do you want to remove?"
if errorlevel 3 (
for /f "tokens=3 delims=," %%a in ("%names%") do (
echo you have choosen to remove %%a
::remove third name in the list
goto start
)
)
if errorlevel 2 ::remove second name in the list
if errorlevel 1 ::remove first name in the list
I've tried using del but that turns out to delete a file in your folder.
I've tried renaming a specific name using set name[%%a]="" but that did nothing.
Any ideas?
Have a look at this example. There are many ways.
#echo off
setlocal enabledelayedexpansion
set names="Bob","Steven","Harry"
for %%i in (%names%) do (
set /a num+=1
set "!num!=%%~i"
)
for /l %%a in (1,1,%num%) do (
set choices=!choices!%%a
echo !num!.!%%a!
)
choice /c 123 /m "please select name to remove"
for /l %%a in (1,1,%num%) do if not "!%%a!"=="!%errorlevel%!" set new_names=!new_names! !%%~a!
echo %new_names:~1%
It can be done without the last for loop as well.. but I opted for it.
Here is some example code, for you to run, and then try to comprehend, I hope it helps rather than confuses:
#Echo Off
SetLocal EnableDelayedExpansion
For /F "Delims==" %%A In ('Set # 2^>NUL') Do Set "%%A="
Set "i=0"
For /F "Delims=:" %%A In ('FindStr "^::" "%~f0" 2^>NUL') Do (
Set /A i+=1
Set "#!i!=%%A"
Echo= !i!. %%A
)
:Ask
Set # 1>NUL 2>&1
If ErrorLevel 1 Echo= Empty names list&Timeout 3 1>NUL&Exit /B
Echo=&Set /P "Option= Choose a name to remove>"
Set #|Findstr "^#%Option%=" 1>NUL||GoTo :Ask
Set "Name=!#%Option%!"
Echo= You've chosen to remove "%Name%"
Timeout 2 1>NUL
Set "#%Option%="
ClS
For /F "Tokens=1*Delims=#=" %%A In ('Set # 2^>NUL') Do Echo= %%A. %%B
GoTo Ask
::Alan
::Beth
::Cleo
::Dale
::Eric
::Faye
::Greg
::Hugh
::Inga
Important note:Please ensure, before saving the above content as a Windows Command Script, that there is a line return, (blank line), at the end.

Running a cmd .bat file with loops and conditions

Hi I am trying to run a scrapy spider using start command from a cmd batch file. I want to run the spider parallely for 10 names from a csv file which contains more than 500 names. So my thought is to basically add some conditions in the loop from 1 to 500 that checks if the 10 command windows have closed, if not then wait for them to close (they auto close after spider is finished). Once the 10 windows which had opened, are closed, open the next 10 and so on. Following is the code I have, i am pretty sure it has big syntax errors. Could you help me debug? Thanks
cd /d "C:\Users\xyz"
for /f "tokens=1,*" %%m in ('tasklist ^| find /I /C "conhost.exe"') do (set var1=%%m)
set counter=1
for /f "usebackq tokens=1 delims=," %%n in ("test.csv") do (
(START /MIN "" scrapy crawl xyz_scraper -a query="%%n" -a pages=20)
set /a counter=counter+1
for /f "tokens=1,*" %%p in ('tasklist ^| find /I /C "conhost.exe"') do (set var2=%%p)
SET /A _result=counter%%10
echo %_result%
IF _result EQU 0 (
:abcd
timeout /t 10
if var2 EQU var1 (
goto bcde
)
ELSE (
goto abcd)
)
:bcde
)
pause
EDIT: deleted the for loop one. Edited the above code based on some suggestions. I don't understand where would i use quotes for variables and where not and how to print a variables value to cmd.
The updated code below:
cd /d "C:\Users\sodhian\sodhi-scraper"
for /f "tokens=1,*" %%m in ('tasklist ^| find /I /C "conhost.exe"') do (set var1=%%m)
echo %var1%
set counter=1
for /f "usebackq tokens=1 delims=," %%n in ("test comp.csv") do (
(START /MIN "" scrapy crawl ind_scraper -a query="%%n" -a pages=20)
set /a "counter=counter+1"
echo %counter%
SET /A _result="counter%%2"
echo %_result%
IF "%_result%" EQU "0" (
:abcd
timeout /t 10
for /f "tokens=1,*" %%p in ('tasklist ^| find /I /C "conhost.exe"') do (set var2=%%p)
echo %var2%
if var2==var1 (
goto bcde
)
ELSE (
goto abcd)
)
:bcde
)
pause
Edit 2:
Based on Stephan's answer. Tried to accomplish what i mentioned in comment of the answer:
setlocal enabledelayedexpansion
set counter=0
for /f "usebackq tokens=1 delims=," %%n in ("test comp.csv") do (
set /a counter+=1
start /MIN "MySpider!counter!" scrapy crawl ind_scraper -a query="%%n" -a pages=20
for /f "tokens=1,*" %%b in ('tasklist /v ^| find /I /C "MySpider"') do (set var1=%%b)
if !var1! geq 5 call :wait
)
:wait
timeout /t 5
for /f "tokens=1,*" %%p in ('tasklist /v ^| find /I /C "MySpider"') do (set var2=%%p)
if !var2! geq 5 call :wait
goto :eof
Changed it to the following: (the /v (verbose option in tasklist was making the above slow)
setlocal enabledelayedexpansion
set counter=0
set max_scrappers=7
for /f "tokens=1,*" %%a in ('tasklist ^| find /C "conhost"') do (set var1=%%a)
for /f "usebackq tokens=1 delims=," %%n in ("test comp.csv") do (
set /a counter+=1
start /min "MySpider!counter!" scrapy crawl ind_scraper -a query="%%n" -a pages=20
for /f "tokens=1,*" %%b in ('tasklist ^| find /C "conhost"') do (set var2=%%b)
set /a var3=!var2!-!var1!
if !var3! geq !max_scrappers! call :wait
)
:wait
for /f "tokens=1,*" %%p in ('tasklist ^| find /C "conhost"') do (set var4=%%p)
set /a var5=!var4!-!var1!
if !var5! geq !max_scrappers! call :wait
goto :eof
As already noted in the comments, labels inside a code block don't work. But you can call a "function", where goto and labels are no problem:
I choosed other numbers and another command to make it work on every system (and faster). Replacing the timeout command with your spider and adapting the numbers should be no problem.
#echo off
setlocal enabledelayedexpansion
REM next line just for generating a testfile:
>"test comp.csv" (for /l %%i in (1,1,10) do echo !random:~-1!)
set counter=0
for /f "usebackq tokens=1 delims=," %%n in ("test comp.csv") do (
set /a counter+=1
start "MySpider!counter!" timeout %%n
if !counter! geq 3 call :wait
)
:wait
tasklist /v|find "MySpider">nul && goto :wait
set counter=0
goto :eof
Searching for conhost is not a good idea, because there could be other processes. Choose an unique window title instead (MySpider in my example) and look for that title (tasklist /v)

findstr not able to accept string from multiple FOR loop

Codes as below:
im getting below error:
FINDSTR: No search strings
I have traced the error an its coming from here:
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" "!FILENAME!" ^| findstr /B "%numbers%"') do (...
Script is working properly if Im replacing FILENAME variable with exact filename manually. But I need to put it in a loop to execute within multiple files..
for /r %%i in (LOG_FILE*.txt) do (
set FILENAME=%%~nxi
for /F "delims=:" %%a in ('findstr /I /N /C:"fin.700 " !FILENAME!') do (
set /A val1=%%a-3, val2=%%a+3, val3=%%a+4, val4=%%a+11, val5=%%a+13 , val6=%%a+29, val7=%%a+30
set "numbers=!numbers!!val1!: !val2!: !val3!: !val4!: !val5!: !val6!: !val7!: "
)
set FILENAME=!FILENAME:~0,-1!
echo !FILENAME!>>tmptmptmp.tmp
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" "!FILENAME!" ^| findstr /B "%numbers%"') do (
set linestr=%%b
echo !linestr!
)
)
Working without the outer FOR loop
#echo off
setlocal EnableDelayedExpansion
setlocal enableextensions
rem Assemble the list of line numbers
set numbers=
if exist "tmp" del "tmp"
if exist "tmp2" del "tmp2"
if exist "tmp.txt" del "tmp.txt"
REM for /r %%i in (LOG_FILE*.txt) do (
REM set FILENAME=%%~nxi
set FILENAME=LOG_FILE14012015.txt
for /F "delims=:" %%a in ('findstr /I /N /C:"fin.700 " !FILENAME!') do (
set /A val1=%%a-3, val2=%%a+3, val3=%%a+4, val4=%%a+11, val5=%%a+13 , val6=%%a+29, val7=%%a+30
set "numbers=!numbers!!val1!: !val2!: !val3!: !val4!: !val5!: !val6!: !val7!: "
)
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" "%FILENAME%" ^| findstr /B "%numbers%"') do (
set linestr=%%b
echo !linestr!
)
Thanks for the help guys. I did a workaround as im not able to force the findstr to work properly with my requirements. Did two separate batch script to handle the logic. Below is a copy in case anyone is interested.
bat file that will handle the outer for loop:
#echo off
setlocal EnableDelayedExpansion
setlocal enableextensions
::Script that will loop for multiple files and will call search.bat
if exist "tmp.txt" del "tmp.txt"
if exist "multiple_search.log" del "multiple_search.log"
#echo Starting search... >> multiple_search.log
for /r %%i in (LOG_FILE*.txt) do (
#echo Searching %%i >> multiple_search.log
call search.bat %%i
)
#echo Search completed... >> multiple_search.log
endlocal
Main bat file:
#echo off
setlocal EnableDelayedExpansion
setlocal enableextensions
set numbers=
if exist "tmp" del "tmp"
if exist "tmp2" del "tmp2"
set FILENAME=%1
for /F "delims=:" %%a in ('findstr /I /N /C:"fin.700 " !FILENAME!') do (
set /A val1=%%a-3, val2=%%a+3, val3=%%a+4, val4=%%a+11, val5=%%a+13 , val6=%%a+29, val7=%%a+30
set "numbers=!numbers!!val1!: !val2!: !val3!: !val4!: !val5!: !val6!: !val7!: "
)
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" "%FILENAME%" ^| findstr /B "%numbers%"') do (
set linestr=%%b
echo !linestr!
echo !linestr!>>tmp
)
set delim==
for /f "tokens=1*" %%a in (tmp) do (
echo %%a|find "!delim!" >nul
if !errorlevel!==0 (echo %%a%%b >> tmp2) else (echo record=%%a%%b >> tmp2)
)
set counter=0
set var=
for /f "tokens=1* delims==" %%a in (tmp2) do (
set /a counter=!counter!+1
set var=!var!%%b
set var=!var: =!
set var=!var!,
if !counter! geq 7 (
echo !var! >> tmp.txt
set counter=0
set var=)
)
endlocal

read then write delimited file - Batch cmd

Hi I have a pipe | delimited file.
I need to reverse all the numbers in there
The file looks like this
Entity|Division|Channel|200|300|800
I need to read the file an create a new one with reversed numbers
Entity|Division|Channel|-200|-300|-800
trying to make this work but, not entirely sure how to amend the text I'm getting. I need help on the :processToken procedure. How to output the tokens into a new file and add "-" and add back the delimiter |
for /f "tokens=* delims= " %%f in (M:\GPAR\Dev\EQ\Upload_Files\eq_test.txt) do (
set line=%%f
call :processToken
)
goto :eof
:processToken
for /f "tokens=* delims=|" %%a in ("%line%") do (
)
if not "%line%" == "" goto :processToken
goto :eof
Thanks
#echo off
set "ent_file=c:\entity.txt"
break>"%tmp%\rev.txt"
for /f "usebackq tokens=1,2,3,4,5,6 delims=|" %%a in ("%ent_file%") do (
echo %%a^|%%b^|%%c^|-%%d^|-%%e^|-%%f
)>>"%tmp%\rev.txt"
move /y "%tmp%\rev.txt" "%ent_file%"
del /q /f "%tmp%\rev.txt"
Should work if your file contains only lines like the one you've posted.
EDIT output the part after the 6th token:
#echo off
set "ent_file=c:\entity.txt"
break>"%tmp%\rev.txt"
for /f "usebackq tokens=1,2,3,4,5,6,* delims=|" %%a in ("%ent_file%") do (
echo %%a^|%%b^|%%c^|-%%d^|-%%e^|-%%f|%%g
)>>"%tmp%\rev.txt"
move /y "%tmp%\rev.txt" "%ent_file%"
del /q /f "%tmp%\rev.txt"
#echo off
setlocal EnableDelayedExpansion
set digits=0123456789
(for /F "delims=" %%f in (eq_test.txt) do (
set "input=%%f"
set "output="
call :processToken
set /P "=!output:~1!" < NUL
echo/
)) > new_file.txt
goto :EOF
:processToken
for /F "tokens=1* delims=|" %%a in ("%input%") do (
set "token=%%a"
set "input=%%b"
)
if "!digits:%token:~0,1%=!" neq "%digits%" set "token=-%token%"
set "output=%output%|%token%"
if defined input goto processToken
exit /B

Resources