2

I am trying to make a simple statistic program which simply counts how many times it's been runned. The problem occurs when I am echo-ing the variable of result of times onto the tracking-file, which are greater than 1. Is it impossible to echo a variable onto a file? What am I missing? :/

if not exist statistic_files (
mkdir statistic_files
)
cd statistic_files

if exist %username%-use_tracking (
FOR /F %%G IN (%username%-use_tracking) DO set /A uses=%%G+1
:: THE PROBLEM IS BELOW
echo %uses% > %username%-use_tracking
cd ..
)

if not exist %username%-use_tracking (
echo 1 > %username%-use_tracking
cd ..
)
Leajian
  • 129
  • 9

6 Answers6

2

Your code is failing when the file already exists because you are setting a variable value and later accessing the value within the same parenthesized code block; %uses% is expanded when the line is parsed, and the entire code block is parsed all at once. So the value you are echoing is the value that existed before the FOR loop is executed - not what you want.

You could solve the problem by enabling delayed expansion using setlocal enableDelayedExpansion, and then using !uses! instead of %uses%. Type help set from a command prompt for more information. Start reading with the paragraph that begins "Finally, support for delayed environment variable expansion has been added..." about halfway through the help.

But there is a simpler way that avoids the need for delayed expansion. You only need to set the value within the IF block. You can access it later outside of the IF block.

There is one other issue that you are currently avoiding, but you should be aware of: 1>filename and >filename do the same thing - they are an instruction to redirect standard output (stdout) to a file. 1 is the id of stdout, and it is the default when a number is not explicitly given. 2>filename redirects standard errror (stderr) to a file. You are avoiding interpretation of the number as the stream id because you have a space between the number and the >. But if you are not careful, you will someday not have a space and be confused as to why the number is not printing. You can avoid the problem by putting the redirection at the front instead of at the end. >filename echo %uses%

So here is a complete working solution.

@echo off
setlocal
if not exist statistic_files mkdir statistic_files
cd statistic_files
set "file=%username%-use_tracking"

set "uses=1"
if exist %file% for /f %%G IN (%file%) do set /A uses=%%G+1
>%file% echo %uses%
dbenham
  • 127,446
  • 28
  • 251
  • 390
1

Testing your current file (just by removing the block at the top that creates the folder, so that it outputs to my current directory, the two cd .. lines, and adding set username=ken at the top) produces:

  • The correct content (a single line with 1) the first time.
  • A file with the single line ECHO is on the second time.
  • An error message the third time.

You're getting that output because ECHO is on, and the second run is ECHOing that setting the second time the batch file is run.

You need to turn echo off at the top of your batch file. (This doesn't prevent using the echo command; it just prevents your batch file itself from being output.)

This works for me (making the changes I mentioned above):

@echo off
set username=ken

if exist %username%-use_tracking (
FOR /F %%G IN (%username%-use_tracking) DO set /A uses=%%G+1
:: THE PROBLEM IS BELOW
echo %uses% > %username%-use_tracking
)

if not exist %username%-use_tracking (
echo 1 > %username%-use_tracking
)

The results were:

  • The correct content (a single line with 1) the first time.
  • A file with the single line 2 the second time.
  • A file with the single line 3 the third time. (And so forth with more runs.)

If you're wanting to keep every line in the file, change the first echo %uses% > %username%-use_tracking to echo %uses% > %username%-use_tracking (note the double >>) instead. One > means overwrite, two means append to the end of current content.

Ken White
  • 123,280
  • 14
  • 225
  • 444
1

Your problem is directly related to Delayed variable Expansion. To solve it, insert this line at beginnng of your program:

setlocal EnableDelayedExpansion

and enclose the variable that was modified inside an IF block in exclamation marks instead percents:

echo !uses! > %username%-use_tracking

For further details, seek for "Delayed Expansion" in this forum; there are plenty answers on the topic, like this one:

Set the variable to another variable in the for statement

Community
  • 1
  • 1
Aacini
  • 65,180
  • 12
  • 72
  • 108
0

you use single >

ie.: echo "Bla" > file.xyz

single > just overwrites anything in the file

try >>

echo "Blabla" >> file.xyz

double >> appends content at the end of the file

RealHowTo
  • 34,977
  • 11
  • 70
  • 85
rgasiore
  • 140
  • 7
0
  1. Use >> instead of > for redirect (append mode).
  2. Use absolute path for log file, instead of relative path, if you are going to change the directory in between.
anishsane
  • 20,270
  • 5
  • 40
  • 73
0

Ok, I have realised that for command affects variables in a strange way and I have to use delayed expansion. Thanks everyone for stating these. I have managed to make it work properly, exactly how I wanted it, here it is :

@echo off
setlocal EnableDelayedExpansion

if not exist statistic_files (
mkdir statistic_files
cd statistic_files
goto skip
)
cd statistic_files
:skip
set "file=%username%-use_tracking"
set "uses=1"

if not exist %file% (
>%file% echo 1
cd ..
)
if exist %file% (
for /f %%G IN (%file%) do set /A uses=%%G+1
>%file% echo !uses!
cd ..
)
Leajian
  • 129
  • 9