Batch probblem: Cant iterate through string variable - for-loop

I'm going crazy for 2 days with this simple task: iterate through a 5 line string with delimiters (or a file with the same content). After I find a delimiter symbol I expect to write that line to the file, then add echo for a cr/lf representation and do next iteration for a next piece of string.
For academic purposes I recreate the code like that:
#echo off
setlocal EnableDelayedExpansion
set "LF=&echo."
set "dlm=+"
set "s1=A A A"
set "s2=B B B"
set "s3=C C C"
set "s4=D D D"
set "s5=E E E"
set "s=%s1%%dlm%%s2%%dlm%%s3%%dlm%%s4%%dlm%%s5%%dlm%"
for /f "tokens=* delims=+" %%i in ("%s%") do (
echo %%i>>test.txt
echo. >>test.txt
)
pause
The output is like:
A A A+B B B+C C C+D D D+E E E+
newline
When I want this
A A A
B B B
C C C
D D D
E E E
When I change token to 1 or 2 it returns the correct line
How to get those sub strings and write them to the file? Why tokens don't work?
I tried to put this into another loop and pass token number as !j!, but it doesn't work. I also thought of a few goto but think it's a bad practice.

The subroutine split_supplied_line in the following batch code snippet should do the trick for (almost) any number of substrings and (almost) any delimiter. No matter whether there is a trailing delimiter.
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "LF=&echo."
set "dlm=+"
set "s1=A A A"
set "s2=B B B"
set "s3=C C C"
set "s4=D D D"
set "s5=E E E"
set "s=%s1%%dlm%%s2%%dlm%%s3%%dlm%%s4%%dlm%%s5%%dlm%"
rem set "s=%s1%%dlm%%s2%%dlm%%s3%%dlm%%s4%%dlm%%s5%" without trailing %dlm%
call :split_supplied_line "%s%" "%dlm%" "72351984test.txt"
goto :eof
:split_supplied_line
set "restofline=%~1" 1st parameter: line to split
set "delims=%~2" 2nd parameter: delimiter character
set "tofile=%~3" 3rd parameter: file where to write
:in_split_supplied_line
call set "noplusline=%restofline:%delims%=%"
if "%noplusline%"=="%restofline%" (
SETLOCAL EnableDelayedExpansion
echo !restofline!>>"%tofile%"
ENDLOCAL
goto :eof
)
for /f "tokens=1,* delims=%delims%" %%i in ("%restofline%") do (
echo %%i>>"%tofile%""
set "restofline=%%j"
)
if ""=="%restofline%" goto :eof
goto :in_split_supplied_line
Output: 2>nul del 72351984test.txt & .\SO\72351984.bat & type 72351984test.txt
A A A
B B B
C C C
D D D
E E E
However, there is a more tricky approach (unfortunately, a trailing delimiter adds a blank line into the output):
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "LF=&echo."
set "dlm=+"
set "s1=A A A"
set "s2=B B B"
set "s3=C C C"
set "s4=D D D"
set "s5=E E E"
set "s=%s1%%dlm%%s2%%dlm%%s3%%dlm%%s4%%dlm%%s5%%dlm%"
rem set "s=%s1%%dlm%%s2%%dlm%%s3%%dlm%%s4%%dlm%%s5%" without trailing %dlm%
>72351984test.txt (echo %s:+=&echo.%)
ENDLOCAL
goto :eof
Output: 2>nul del 72351984test.txt & .\SO\72351984a.bat & type 72351984test.txt
A A A
B B B
C C C
D D D
E E E

With "Tokens=*" you request the for-loop to return everything it finds into the first parameter, and in doing so completely ignore the delims-clause.
What you want is the first 5 tokens, separated by delimiter "+".
for /f "tokens=1-5 delims=+" %%i in ("%s%") do >>test.txt (
echo %%i
echo %%j
echo %%k
echo %%l
echo %%m
echo.
)

Related

Lower and remove special character of file name

I want to create a script, which lower and remove special character multiple text files.
my files in folder like this:
- ⚡ Document.txt
- [Review] Test File.txt
i want remove special char of filename like this
- document.txt
- review test file.txt
i've tried like this, but only lower filename. how to remove special character?
#echo off
setlocal enableDelayedExpansion
pushd %currentfolder%
for %%f in (*) do (
set "filename=%%~f"
for %%A in (a b c d e f g h i j k l m n o p q r s t u w x y z) do (
set "filename=!filename:%%A=%%A!"
)
ren "%%f" "!filename!" >nul 2>&1
)
endlocal
Before
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=u:\your files"
set "validchars=abcdefghijklmnopqrstuvwxyz1234567890. "
pushd "%sourcedir%"
FOR %%b IN (*) DO (
set "newname="
set "oldname=%%b"
call :validate
if /i "%%b" neq "!newname!" ren "%%~sb" "!newname!"
)
popd
GOTO :EOF
:validate
if not defined oldname goto :eof
set "c1=%oldname:~0,1%"
set "oldname=%oldname:~1%"
if "!validchars:%c1%=!" neq "%validchars%" set "newname=%newname%%c1%"
goto validate
Always verify against a test directory before applying to real data.
I predict it will have problems with some unicode characters and the usual suspects.
You could use pure powershell for this, or if you feel like continuing the use batch-file, just call powershell to assist:
#echo off
for %%i in (*) do for /f "delims=" %%a in ('powershell "$string='%%~ni';$String.tolower() -replace '[\W]', ''"') do echo ren "%%~i" "%%a%%~xi"
Note the echo at the end of the line, that is to test functionality by printing to screen before you do the actual renaming. Only remove echo when you're happy with the printed results.

Merge Two text files batch script

I need to have a batch file to combine two text files. My files are:
file1.txt
1
2
3
4
file2.txt
A
B
C
D
E
F
G
H
I
J
K
L
I need to get mixfile that looks like this
mix.txt
1
A
B
C
D
2
E
F
G
H
3
I
J
K
L
Note: for each line of file 1 I need 4 lines from file 2. 1->1-4,2->5-8, 3->9-12
I tried this program (bat file solution), but donot know, how to modify it to get the results with my requirements.
#echo off
set f1=file1.txt
set f2=file2.txt
set outfile=mix.txt
type nul>%outfile%
(
for /f "delims=" %%a in (%f1%) do (
setlocal enabledelayedexpansion
set /p line=
echo(%%a!line!>>%outfile%
endlocal
)
)<%f2%
pause
You will need some kind of line counter:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Improved quoted `set` syntax:
set "f1=file1.txt"
set "f2=file2.txt"
set "outfile=mix.txt"
set "quotient=4"
rem // Redirect the whole block once:
< "%f1%" > "%outfile%" (
rem // Temporarily precede each line with a line number:
for /f "delims=" %%a in ('findstr /N "^" "%f2%"') do (
rem // Get modulo of the line number:
set "item=%%a" & set /A "num=(item+quotient-1)%%quotient"
setlocal EnableDelayedExpansion
rem // Return a line from the other file only if modulo is zero:
if !num! equ 0 (
set "line=" & set /P line=""
echo(!line!
)
rem // Return current line with the line number prefix removed:
echo(!item:*:=!
endlocal
)
)
endlocal
pause
The length of file2.txt determines how many iterations occur.

Make variable all lower case in batch without call

Here is a code I found to make a value lower case, but was curious how to do it without the CALL and just use a variable:
SET String=Hello, how are you ?
CALL :LoCase String
:LoCase
FOR %%i IN ("A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i" "J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r" "S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z") DO CALL SET "%1=%%%1:%%~i%%"
GOTO:EOF
I'm not sure which call you want to avoid - the one in the for loop or calling the subroutine. But it is possible to ditch both with macro and delayedExpansion which will be executed much faster:
#echo off
set locase=for /L %%n in (1 1 2) do if %%n==2 ( for %%# in (a b 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 set "result=!result:%%#=%%#!") ELSE setlocal enableDelayedExpansion ^& set result=
set "string=SOme STrinG "
%locase%%string%
echo %result%
Also because of the way how replacement works in batch files you can just list the lower case letters - this will increase the performance also. Probably the code above is the fastest possible way to set string in lowercase only in batch file.
If you don't wish to use Call at all, convert the function to a macro.
Here's a macro that can convert upper or lower
#ECHO OFF & Setlocal DISABLEDelayedExpansion
(Set \n=^^^
%=Do Not Modify=%
)
(Set LF=^
%=Do Not Modify=%)
Set CASE=For %%n in (1 2) Do IF %%n==2 (%\n%
Set "Switch=?"%\n%
If not "[?]" == "[!Sub!]" (%\n%
If "!String!" == "" Echo/No args Input. %%Case:?=!Switch!%%!String!!LF! !Usage!%\n%
For /F "Tokens=1,2 Delims={}" %%G in ("!String!")Do If not "%%~H" == "" (%\n%
Set "String=%%~H"%\n%
Set "upper=!Switch:-u=!" ^& If Not "!upper!" == "!switch!" For %%x in (A B 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 (Set "String=!String:%%x=%%x!")%\n%
Set "lower=!Switch:-l=!" ^& If Not "!lower!" == "!switch!" For %%x in (a b 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 (Set "String=!String:%%x=%%x!")%\n%
Set "supress=!Switch:-s=!" ^& If "!supress!" == "!switch!" Echo/!String!%\n%
Set "%%~G=!String!"%\n%
) Else Echo Missing Arg. %%Case:?=!Switch!%%{%%G}{%%H}!LF! !Usage!%\n%
) Else echo/Substring Switch required. %%Case:?=!Switch!%%!String!!LF! !Usage!%\n%
) Else Set String=
Set "SUB=?"
Set "Usage=Usage: %%CASE:?=[-U|-L]{-S}%%{ReturnVar}{Input String}"
Setlocal ENABLEDelayedExpansion
rem /* usage examples */
Set /P "iString=String: "
rem /* convert upper */
%CASE:?=-u%{string.upper}{!iString!}
rem /* convert lower; suppress output */
%CASE:?=-l -s%{string.lower}{!iString!}
rem /* Demonstrate Macro error handling */
%CASE:?=-l%
%CASE%{string.noswitch}{!iString!}
%CASE:?=-l%{String.false}
Set string.
It is easy enough to use the ToLower() function in PowerShell. If you are on a supported Windows platform, PowerShell is available. It runs on Linux and Mac.
SET "STRING=Hello, how are you ?"
FOR /F "delims= eol=" %A IN ('powershell -NoLogo -NoProfile -Command "'%STRING%'.ToLower()"') DO (SET "STRING=%~A")
ECHO STRING is now === %STRING%
If this goes into a .bat file script, double the PERCENT character on the FOR loop variable.
SET "STRING=Hello, how are you ?"
FOR /F "delims= eol=" %%A IN ('powershell -NoLogo -NoProfile -Command "'%STRING%'.ToLower()"') DO (SET "STRING=%%~A")
ECHO STRING is now === %STRING%
It will process a string containing a QUOTATION MARK character.
C:\src\t>SET "STRING=Now Is ""The Time"
C:\src\t>FOR /F "delims= eol=" %A IN ('powershell -NoLogo -NoProfile -Command "'%STRING%'.ToLower()"') DO (SET "STRING2=%~A")
C:\src\t>(SET "STRING2=now is "the time" )
C:\src\t>SET STRING
STRING=Now Is ""The Time
STRING2=now is "the time
If the code is written in PowerShell, it could be:
$String = "Hello, how are you?".ToLower()
Write-Output $String
DIR /L works for all filenames, even Unicode like ÁCCÉNTS.
The macro $lower is defined by:
set ^"$lower=(dir/l/b/o-d "%TEMP%"^|"%ComSpec%" /v/c"set/plower=&echo(!lower!") 9^>\\?\"%TEMP%"\"
where DELAYEDEXPANSION must be DISABLED.
A demonstration script:
#echo off
chcp 65001 >nul
====SETLOCAL DisableDelayedExpansion EnableExtensions
set "PATH="
set "DPATH="
set "PATHEXT=;"
set ^"$lower=(dir/l/b/o-d "%TEMP%"^|"%ComSpec%" /v/c"set/plower=&echo(!lower!") 9^>\\?\"%TEMP%"\"
echo START: %time%
%$lower%"Hello, how are you"
%$lower%"I print a string (VALID FILENAME) in lower case"
%$lower%"ABCDEFGHIIJKLMNOPQRSTUVWXYZ"
%$lower%"GreekΘ&Áccents work"
%$lower%"ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ"
%$lower%"µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßĀĂĄĆĈĊČĎĐĒĔĖĘĚĜĞĠĢĤĦĨĪĬĮİİIJĴĶĹĻĽĿŁŃŅŇʼnŊŌŎŐŒŔŖŘŚŜŞŠŢŤŦŨŪŬŮŰŲŴŶŸŹŻŽ"
%$lower%" SPA CES "
%$lower%"EXCLAMS!!!"
%$lower%";SEMICOLONS all work"
%$lower%"SADLY ILLEGAL FILENAMES DON'T"
%$lower%"\/:*?<>|"
echo END: %time%

Windows batch: in a string, how to add one spaces between characters?

in a batch I have %cd% that is a path, for example c:\docu
I need to generate in a new enviroment variable c : \ d o c u
How can I do? thanks
Make a bat file called AddSpace.bat:
AddSpace.bat
#echo off
setlocal enabledelayedexpansion
set v=%*
set var=
set /a l=0
:loop
set c=!v:~%l%,1!
if "%c%"=="" goto end
set "var=%var%%c% "
set /a l+=1
goto loop
:end
Echo %var%
:: You can do whatever you want with the variable here.
endlocal
Use
C:\>>AddSpace.bat abcdef
a b c d e f
C:\>>AddSpace.bat C:\Program Files
C : \ P r o g r a m F i l e s
C:\>>AddSpace.bat Hi This Message Has Spaces
H i T h i s M e s s a g e H a s S p a c e s
C:\>>AddSpace.bat Lots of Spaces here -
L o t s o f S p a c e s h e r e -
C:\>>AddSpace.bat C:\docu
C : \ d o c u
C:\>>
NB As you can see, it will also double spaces. This is easy to fix, simply add:
if "%c%"==" " goto loop
just after if "%c%"=="" goto end
#echo off
call :strlen0 cd len
echo %len%
set "final="
setlocal EnableDelayedExpansion
for /l %%# in (0,1,%len%) do (
set "letter_with_space=!cd:~%%#,1! "
set final=!final!!letter_with_space!
)
endlocal && (
set "final=%final%"
)
echo %final%
exit /b 0
:strlen0 StrVar [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%N,1!" neq "" (
set /a "len+=%%N"
set "s=!s:~%%N!"
)
)
endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b
It's done over %cd% as this is what you requested but it can be done over any variable.strlen subroutine is taken from here : http://ss64.org/viewtopic.php?id=424

windows 7 remaining batch file arguments

In windows batch files, I know %1 is replaced with the first argument, %2 is replaced with the 2nd argument and %* is replaced with all arguments.
Is there a way to get all the arguments after the 1st one? (e.g. arguments 2-N) What about all the arguments after the 2nd one?
The SHIFT command doesn't seem to affect %*.
#ECHO OFF
SETLOCAL
CALL :allafter 3 %*
ECHO args=%args%
GOTO :eof
:allafter
FOR /l %%a IN (1,1,%1) DO SHIFT
(SET args=)
:argloop
shift
IF NOT .%1==. SET args=%args% %1&GOTO argloop
IF DEFINED args SET args=%args:~1%
GOTO :eof
to get everything after the 3rd argument to ARGS
Edit - to take care of space-separated elements which may include commas
#ECHO OFF
SETLOCAL
CALL :allafter 3 %*
ECHO args=%args%
CALL :allafter2 3 %*
ECHO args=%args%
GOTO :eof
:allafter
FOR /l %%a IN (1,1,%1) DO SHIFT
(SET args=)
:argloop
shift
IF NOT .%1==. SET args=%args% %~1&GOTO argloop
IF DEFINED args SET args=%args:~1%
GOTO :EOF
:allafter2
SET /a count=%1
SET args=%*
:arg2loop
SET oldargs=%args%
call SET args=%%args:*%1 =%%
IF "%args%"=="%oldargs%" (call SET args=%%args:*%1,=%%) ELSE (SET /a count-=1)
shift
IF %count% gtr -1 GOTO arg2loop
GOTO :EOF
Hmm- spoke too soon. This modified routine should play nicer. The previous version treated what was to be defined as one argument one,two,three as three separate arguments when the remove-leading-n was invoked.
Well, there is a way-ish...
By using the /n switch on the shift command, you can sort of do something like it. However, it will delete all of the argument and put them into a certain variable (so you can't call %3 anymore without a for loop).
#setlocal enableextensions
#echo off
:loop
if "%~2" equ "" goto end
set variable=%variable% %~2
shift /2
goto loop
:end
echo %1
echo %variable%
endlocal
To separate the parameters again just do a simple for loop (I'm sure you can find documentation on it somewhere).
Solution without shift & goto:
#echo off &setlocal enabledelayedexpansion
set /a count=0
for %%i in (%*) do set /a count+=1
set "args="
for /l %%i in (2,1,%count%) do if defined args (call set "args=!args! %%%%i") else call set "args=%%%%i"
echo.%args%
endlocal
> type t.bat
#echo off
echo %*
for /f "tokens=1,*delims= " %%i in ("%*") do echo %%j
> t a b c d e f,g h i
a b c d e f,g h i
b c d e f,g h i
> t a,a b c d e f,g h i
a,a b c d e f,g h i
b c d e f,g h i
>

Resources