DOS Batch setting variable ignores a ! char

Microsoft Excel 2003 (full product)
April 1, 2011 at 12:02:17
Specs: windows server standard without hyperv
I have a dos batch that loops thru all files in a folder and reads all records in each file.

My loop is like this

setlocal EnableDelayedExpansion
for %%a in (d:\testdir\*.*) do (
echo %%a
for /f "tokens=1 delims=" %%b in (%%a) do (
set rec1=%%b
set name=!rec1:~7,19!
set testnum=!rec1~26,5!
)...
...
The problem is that if rec1 contains a ! character, it's ignored and when I set the variables they're off by 1.

For example

If rec1 = "0123456This is a test rec!12345" -- testnum = "2345 ", name = "This is a test rec1"

If rec1 = "0123456This is a test recx12345" -- testnum = "12345" name = "This is a test recx"

It's fine for all records with no !. So what's up?? Any ideas?

Thanks for anything.


See More: DOS Batch setting variable ignores a ! char

Report •


#1
April 2, 2011 at 14:29:32
When setlocal EnableDelayedExpansion is coded the ! char becomes a special symbol and is ignored in string setting. There are ways to workaround this issue, mainly used in NT/4.0 that lacks the delayed expansion facility, as the use of the call statetement with internal subroutines, but the whole structure of your batch needs to be posted to perform the conversion.

By the way this is NT batch nothing to do with DOS that doesn't exist in Windows Server.


Report •

#2
April 2, 2011 at 15:49:14
Without seeing the whole script I don't know if this trick is applicable, it's good if you want to output to file, bad if you want to keep the set variables. It essentially makes the variables local to each iteration.

@echo off
setlocal DisableDelayedExpansion
for %%a in (d:\testdir\*.*) do (
    for /f "tokens=1 delims=" %%b in (%%a) do (
        set rec1=%%b
        setlocal EnableDelayedExpansion
        set name=!rec1:~8,19!
        set testnum=!rec1:~27,5!
        echo !Name!
        echo !testnum!
        endlocal
    )
)
pause


Report •

#3
April 5, 2011 at 09:51:25
Thank you for the information. This post was under the dos forum, but a moderator moved it here.

I think Judago is on the right track, but I need to keep variable values between iterations.

1) Is there a way to replace the ! with x or something before it ignores the ! at the point of set rec1=%%b. I found a replace syntax (set var2=%var1:!=x%) , but I don't know how to use it with all the %'s and delayedexpansion's.

2) maybe there is another way to do what I want - I need to

read all recs in all files in given folder
for each rec set a date and qty value based on record position. Sum qty.
output date, count, sumqty when date changes
output total count, totalqty when all files read

My batch does the above correctly except when it encounters the ! in a record. Then the positions are off, so the set data values are wrong.

Thanks.


Report •

Related Solutions

#4
April 5, 2011 at 13:16:07
Or if I could tell that there was a ! in a position, I could set my variables over 1 position. Is there a way to find a ! with enabledelayed expansion set?

Report •

#5
April 5, 2011 at 14:04:37
Can you please post the whole original script?

Is there a way to find a ! with enabledelayed expansion set?

Not really, because it will expand a variable/disappear inside the for variable.


Report •

#6
April 6, 2011 at 14:33:19
Here is the whole script:

rem @echo off
cd "c:\testdir"
set logfile=verify.log
set month=06
echo Month to check: %month% >> %logfile%
echo ' >> %logfile%
rem
set moncount=0
set monunits=0
rem
setlocal EnableDelayedExpansion
echo Date SaleCount UnitTot >> !logfile!
for %%a in ("file_!month!*.txt") do (
set daycount=0
set dayunits=0
set savedate=0
set first=1
echo %%a
echo %%a >> !logfile!
for /f "tokens=1 delims=" %%b in (%%a) do (
set rec1=%%b
set whlsaler=!rec1:~0,7!
set cust=!rec1:~7,6!
set units=!rec1:~163,6!
set return=!rec1:~179,1!
set date=!rec1:~180,6!
set item=!rec1:~169,10!
set address=!rec1:~13,126!
set invoice=!rec1:~186,15!
set ship=!rec1:~218,15!
set item2=!rec1:~233,13!
rem
rem echo savedate !savedate! date !date! >> !logfile!
if not !savedate!==!date! (if not !first!==1 echo !savedate! !daycount! ! dayunits! >> !logfile!)
if not !savedate!==!date! set daycount=0
if not !savedate!==!date! set dayunits=0
set savedate=!date!
set first=0
set /a daycount=!daycount!+1
set /a moncount=!moncount!+1
if !return!==- set /a units=!units!*-1
set /a dayunits=!dayunits!+!units!
set /a monunits=!monunits!+!units!
)
rem write out data for last rec in this file
echo !savedate! !daycount! !dayunits! >> !logfile!)
)
echo ---------------------------------------- >> !logfile!
echo MonthlySaleCount MonthlyUnitTot >> !logfile!
echo !moncount! !monunits! >> !logfile!
echo ' >> !logfile!
echo ' >> !logfile!

It's just a simple double loop putting out some statistics, nothing fancy.


Report •

#7
April 6, 2011 at 16:18:35
I haven't tested this(no test data) but I think it will be ok, I also changed "date" to
"rdate" because %date%/!date! is a dynamic system variable.

I used the trick posted above with the addition a another
for loop to return the variables needed between iterations.

rem @echo off
setlocal DisableDelayedExpansion

cd "c:\testdir"
set logfile=verify.log
set month=06
echo Month to check: %month% >> %logfile%
echo ' >> %logfile%
rem
set moncount=0
set monunits=0
rem

echo Date SaleCount UnitTot >> !logfile!
for %%a in ("file_!month!*.txt") do (
    set daycount=0
    set dayunits=0
    set savedate=0
    set first=1
    echo %%a
    echo %%a >> !logfile!
    for /f "tokens=1 delims=" %%b in (%%a) do (
        set rec1=%%b
        SetLocal EnableDelayedExpansion
        set whlsaler=!rec1:~0,7!
        set cust=!rec1:~7,6!
        set units=!rec1:~163,6!
        set return=!rec1:~179,1!
        set rdate=!rec1:~180,6!
        set item=!rec1:~169,10!
        set address=!rec1:~13,126!
        set invoice=!rec1:~186,15!
        set ship=!rec1:~218,15!
        set item2=!rec1:~233,13!
        rem
        rem echo savedate !savedate! date !rdate! >> !logfile!
        if not !savedate!==!rdate! (
            if not !first!==1 echo !savedate! !daycount! ! dayunits! >> !logfile!
        )
        if not !savedate!==!rdate! set daycount=0
        if not !savedate!==!rdate! set dayunits=0
        set savedate=!rdate!
        set first=0
        set /a daycount=!daycount!+1
        set /a moncount=!moncount!+1
        if !return!==- set /a units=!units!*-1
            set /a dayunits=!dayunits!+!units!
            set /a monunits=!monunits!+!units!
        )
        rem write out data for last rec in this file
        echo !savedate! !daycount! !dayunits! >> !logfile!
        for /f "tokens=1-8 delims=+" %%c in ("!units!+!moncount!+!monunits!+!daycount!+!dayunits!+!first!+!savedate!+!rdate!" do (
            endlocal
            set moncount=%%c
            set monunits=%%d
            set daycount=%%e
            set dayunits=%%f
            set first=%%g
            set savedate=%%h
            set rdate=%%i
        )
    )
)
SetLocal EnableDelayedExpansion
echo ---------------------------------------- >> !logfile!
echo MonthlySaleCount MonthlyUnitTot >> !logfile!
echo !moncount! !monunits! >> !logfile!
echo ' >> !logfile!
echo ' >> !logfile!
endlocal
endlocal


Report •

#8
April 12, 2011 at 15:02:45
Thanks, but your script doesn't work. One of the reasons is that you can't

"set rec1=%%b "

before the delayed expansion because rec1 is then set to nothing. It's blank before you enter the loop. So there was nothing in the %%c at

"for /f "tokens=1-8 delims..." But it was a good idea. I tried to get it work, but not successful.

Also, I needed to write out sums within the loop when the date changed and that wasn't working.

So, I came up with the script below that strips out the ! as part of setting %%b. It sets up the incoming record to the loop with no ! in it, but it will only work if you know you have 1 or no !'s. If you could have multiple !,s then I guess you could do the same with up to 20 or so tokens. It will work for me to assume 1 !.


set logfile=testlog.txt
setlocal enabledelayedexpansion
rem
for %%a in ("file_06*.txt") do (
echo %%a >> !logfile!
rem
rem strip out the ! and get 2 parts (if no ! then all of rec is in %%b)
for /f "tokens=1,2* delims=!" %%b in (%%a) do (
rem put both parts together to see what I've got (%%c might be blank)
echo %%b %%c >> !logfile!
rem
if "%%c"=="" (
echo c is blank >> !logfile!
set rec1=%%b
echo !rec1! >> !logfile!
) else (
set rec1=%%b %%c
echo !rec1! >> !logfile!
)
rem Now I have rec1 setup and I can proceed with my original script here
)
)



Report •

#9
April 12, 2011 at 16:04:36
I didn't have any full test data, without testing there is always a risk it won't work ;).


before the delayed expansion because rec1 is then set to nothing. It's blank before you enter the loop. So there was nothing in the %%c at

Actually setlocal inherits all variables, it's endlocal(also implicitly called at exit) that resets the values.


Anyhow, I'm glad you got it to work.


Report •

Ask Question