1

I am fighting with little piece of code for last two days. In this I am not able to set variable in a for loop. I want to assign a filename to a variable for string manipulation.

echo off
for  /f  %%a IN ('dir /b *_ah.ttf') DO (
set /a fName=%%~na 
echo %fName% 
)

When I echo fName variable I get only last filename repeatedly number of times for for loop count.

(I want to pass this variable as an argument to some batch file as follows

ttfhnt --strong-stem-width=D -i %%a  %fName:~0,-3%.ttf

but its failing due to above problem)

Can somebody help me please?

1
  • there's no for /f in DOS. It's only available in Windows cmd. They're very different Commented Jan 27, 2019 at 11:33

1 Answer 1

1

When the cmd parser reads a line or a block of lines (the code inside the parenthesis), all variable reads are replaced with the value inside the variable before starting to execute the code. If the execution of the code in the block changes the value of the variable, this value can not be seen from inside the same block, as the read operation on the variable does not exist, as it was replaced with the value in the variable.

This same behaviour is seen in lines where several commands are concatenated with &. The line is fully parsed and then executed. If the first commands change the value of a variable, the later commands can not use this changed value because the read operation replace.

To solve it, you need to enable delayed expansion, and, where needed, change the syntax from %var% to !var!, indicating to the parser that the read operation needs to be delayed until the execution of the command.

And set /A is only used for arithmetic operations

setlocal enabledelayedexpansion
for  /f "delims=" %%a IN ('dir /b *_ah.ttf') DO (
    set "fName=%%~na"
    echo "!fName!" "!fName:~0,-3!"
)

edited to adapt to comments

While for command is able to execute a command (in the OP code, the dir...), retrieve its output and then iterate over the lines in this output, the original reason for the command is to iterate over a set of files. In this form, the code can be written as

setlocal enabledelayedexpansion
for  %%a IN ("*_ah.ttf") DO (
    set "fName=%%~na"
    echo "!fName!" "!fName:~0,-3!"
)

Now, the for command replaceable parameter will iterate over the indicated set of files. (execute for /? for a list of all the command options).

But as foxidrive points, the problem with delayed expansion are the exclamation signs. Without delayed expansion, they are another normal character, but with delayed expansion they frequently become a problem when a value containig them is assigned/echoed.

A quick test

@echo off
    setlocal enabledelayedexpansion

    set "test=this is a test^!"
    echo ---------------------
    set test
    echo ---------------------
    echo delayed : !test!
    echo normal  : %test%
    for /f "delims=" %%a in ("!test!") do echo for     : %%a

Will show

---------------------
test=this is a test!
---------------------
delayed : this is a test!
normal  : this is a test
for     : this is a test

Obviously when the value is a file name, this behaviour will make the code find or not the file.

Depending on the case different solutions can be used, but usually it involves the activation / desactivation of the delayed expansion behaviour (beware, the endlocal removes any change in environment variables from the previous setlocal).

@echo off
    setlocal enabledelayedexpansion

    set "test=this is a test^!"
    echo ---------------------
    set test
    echo ---------------------
    echo delayed : !test!

    rem Commuted to no delayed expansion
    setlocal disabledelayedexpansion
    echo normal  : %test%
    endlocal

    rem Cancelled the initial enable delayed expansion
    for /f "delims=" %%a in ("!test!") do endlocal & echo for     : %%a

    rem The last endlocal has removed the changes to the variable
    echo no data : [%test%]
Sign up to request clarification or add additional context in comments.

5 Comments

+1 Just commenting here that filenames containing ! will fail due to delayed expansion. If the filenames have only one _ then this can be handled another way.
Awesome MC ND, It works perfectly. Thank you very very much for quick answer and detailed explanation. I will read your answer several times to fit into my little brain. By the way thank speising also for the minute observation.
@speising for some reason (*_ah.ttf) did not work for me. But anyway thanks for your comment.
speising's comment is accurate - it will work fine if you follow what he is saying.
I never thought it is that much complicated. Your answer is really much helpful than my task required. I will try to implement this logic in my script. Great work. Merci. @foxidrive, yeah he must be right, but it seems I could not use it correctly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.