Forfiles in CMD file

Microsoft Windows server 2003 r2 enterpr...
March 8, 2010 at 16:04:26
Specs: Windows XP
I cannot determine why the following does not work . . .

D:\DB2\Admin\ImageDDL>type test.cmd
@echo off
set DBID=bigdb
set DBPW=pwd

forfiles /m tes*.sql /c "cmd /c call :ChgFile @file"
exit /b

:ChgFile %1
if exist work.sql del work.sql
for /f "tokens=*" %%a in (%1) do call :ChgText "%%a
del %1
rename work.sql %1
exit /b

:ChgText %1
set Text=%~1%
echo %Text% >> work.sql
exit /b
D:\DB2\Admin\ImageDDL>test.cmd

Invalid attempt to call batch label outside of batch script.

I am trying to substitute tokens in sql files before sending them to the DB2 CLP for processing.

Thus the code attempts to get all files that match a filespec using forfiles, then read through the file line by line using the for command (ChgFile label). Then do environment variable substitution (ChgText label) on the file and replace the original file (rename).

If I extract the ChgFile code to work on a single file it works, but when I try to include it in the forfiles command I get the message "Invalid attempt to call batch label outside of batch script." as indicated above.

Please help if you can . . .


See More: Forfiles in CMD file

Report •


#1
March 8, 2010 at 16:24:49
what is "forfiles"? i've never heard of it. i'm 99 percent sure it's not a native xp command, so is it a third-party app?
generally, for /f is used to iterate through a file or a directory, which you already know since your script is using it.
My guess is that is source of the problem, other than unclosed quotes on lines 6 & 11:
:6
forfiles /m tes*.sql /c "cmd /c call :ChgFile @file"
:11
for /f "tokens=*" %%a in (%1) do call :ChgText "%%a

ps: might want to post this in the "programming" forum. most batch-script questions are directed there...



Report •

#2
March 8, 2010 at 22:50:35
nbrane: what is "forfiles"? i've never heard of it. i'm 99 percent sure it's not a native xp command, so is it a third-party app?
It's part of one of the Windows Resource Kits. As to how it works, think Linux/UNIX's find.

rgdingman: Invalid attempt to call batch label outside of batch script.
FORFILES, not CMD, runs the command. (Basically, think of it as spawning a new instance of CMD for every file it finds.) This "breaks" it out of the scope of your script. Additionally, you spawn your own copy of CMD from FORFILES, breaking you out of the script a second time. The path of least resistance would be to make that procedure its own standalone script, and call THAT from FORFILES.


Report •

#3
March 9, 2010 at 02:06:51
Seems obvious, you call a label in a new DOS-box ... label is unknown (in that DOS-box) and errors out.

If you really make it work with calls, just call another BAT/CMD file instead of a label. IMO, labels are a PITA, and they also teach you bad programming.


Report •

Related Solutions

#4
March 9, 2010 at 08:09:59
Can one of you give me an example of calling the standalone script from FORFILES?

I have tried this with a different problem - the value of %1 from the CMD in FORFILES gets passed to the label ChgText instead of the %%a value (input line). I cannot understand this change of behavior and assume it is because I have coded the calling of the standalone script wrong in the FORFILES command.

Here is what I had coded . . .

@echo off
set DBID=bigdb
set DBPW=pwd

forfiles /m tes*.sql /c "cmd /c call change_tokens.cmd @file"
exit /b


I also tried . . .

@echo off
set DBID=bigdb
set DBPW=pwd

forfiles /m tes*.sql /c "cmd /c change_tokens.cmd @file"
exit /b


Report •

#5
March 9, 2010 at 08:54:00
Yes, something like that, but we cannot tell from here what parameters "change_tokens.cmd" accepts (and what not). If that is "@file", then all is OK. But, typically, you would be calling something with variable parameters, like :

change_tokens.cmd %my_variable%

Just try to get the format of data that FORFILES returns (we don't know, it's not Windows). You can easily test :

forfiles /m tes*.sql /c echo @file - %my_variable% - %whatever%

Find what is holding the variable you are looking for. Know that Windows uses % signs to delimit variables. The @ sign I cannot link to any such related behaviour, it's a hardcoded character for Windows (no?)


Report •

#6
March 9, 2010 at 08:56:51
the value of %1 from the CMD in FORFILES gets passed to the label ChgText instead of the %%a value
I see neither %1 nor %a in your script there.

Report •

#7
March 9, 2010 at 08:58:36
tvc: (we don't know, it's not Windows).
It is, however, Microsoft. So it has its own TechNet page.

Report •

#8
March 9, 2010 at 09:10:24
Here is change_tokens.cmd . . .

rem @echo off
if exist work.sql del work.sql
copy %1 %1.bkp
for /f "tokens=*" %%a in (%1) do call :ChgText "%%a
del %1
rename work.sql %1
exit /b

:ChgText %1
set Text=%~1%
echo %Text% >> work.sql
exit /b


Report •

#9
March 9, 2010 at 09:13:52
and the calling script . . . (test.cmd)

rem @echo off
set DBID=bigdb
set DBPW=pwd

forfiles /m tes*.sql /c "change_tokens.cmd @file"
exit /b


Report •

#10
March 9, 2010 at 09:20:01
> It is, however, Microsoft. So it has its own
> TechNet page.
>

Windows 2008 apparently ... interesting.


Report •

#11
March 9, 2010 at 09:22:32
Dingman, that FORLOOP stuff seems to be doing the same as a FOR /F loop, but in a more complex way ...

FOR /F "eol=; tokens=2,3* delims=, " %i in (myfile.txt) do @echo %i %j %k

would parse each line in myfile.txt, ignoring lines that begin with
a semicolon, passing the 2nd and 3rd token from each line to the for
body, with tokens delimited by commas and/or spaces. Notice the for
body statements reference %i to get the 2nd token, %j to get the
3rd token, and %k to get all remaining tokens after the 3rd. For
file names that contain spaces, you need to quote the filenames with
double quotes. In order to use double quotes in this manner, you also
need to use the usebackq option, otherwise the double quotes will be
interpreted as defining a literal string to parse.

%i is explicitly declared in the for statement and the %j and %k
are implicitly declared via the tokens= option. You can specify up
to 26 tokens via the tokens= line, provided it does not cause an
attempt to declare a variable higher than the letter 'z' or 'Z'.
Remember, FOR variables are single-letter, case sensitive, global,
and you can't have more than 52 total active at any one time.

You can also use the FOR /F parsing logic on an immediate string, by
making the filenameset between the parenthesis a quoted string,
using single quote characters. It will be treated as a single line
of input from a file and parsed.

Finally, you can use the FOR /F command to parse the output of a
command. You do this by making the filenameset between the
parenthesis a back quoted string. It will be treated as a command
line, which is passed to a child CMD.EXE and the output is captured
into memory and parsed as if it was a file. So the following
example:

FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i

would enumerate the environment variable names in the current
environment.

In addition, substitution of FOR variable references has been enhanced.
You can now use the following optional syntax:

%~I - expands %I removing any surrounding quotes (")
%~fI - expands %I to a fully qualified path name
%~dI - expands %I to a drive letter only
%~pI - expands %I to a path only
%~nI - expands %I to a file name only
%~xI - expands %I to a file extension only
%~sI - expanded path contains short names only
%~aI - expands %I to file attributes of file
%~tI - expands %I to date/time of file
%~zI - expands %I to size of file
%~$PATH:I - searches the directories listed in the PATH
environment variable and expands %I to the
fully qualified name of the first one found.
If the environment variable name is not
defined or the file is not found by the
search, then this modifier expands to the
empty string

The modifiers can be combined to get compound results:

%~dpI - expands %I to a drive letter and path only
%~nxI - expands %I to a file name and extension only
%~fsI - expands %I to a full path name with short names only
%~dp$PATH:I - searches the directories listed in the PATH
environment variable for %I and expands to the
drive letter and path of the first one found.
%~ftzaI - expands %I to a DIR like output line

In the above examples %I and PATH can be replaced by other valid
values. The %~ syntax is terminated by a valid FOR variable name.
Picking upper case variable names like %I makes it more readable and
avoids confusion with the modifiers, which are not case sensitive.


Report •

#12
March 9, 2010 at 09:35:58
Changed the calling code to . . .

forfiles /m tes*.sql /c "cmd /c d:\db2\admin\imageddl\change_tokens.cmd @file"


Here are the echo outs . . .

D:\DB2\Admin\ImageDDL>test2.cmd

D:\DB2\Admin\ImageDDL>rem @echo off

D:\DB2\Admin\ImageDDL>set DBID=bigdb

D:\DB2\Admin\ImageDDL>set DBPW=pwd

D:\DB2\Admin\ImageDDL>forfiles /m tes*.sql /c "cmd /c d:\db2\admin\imageddl\change_tokens.cmd @file"


D:\DB2\Admin\ImageDDL>rem @echo off

D:\DB2\Admin\ImageDDL>if exist work.sql del work.sql

D:\DB2\Admin\ImageDDL>copy "test.sql" "test.sql".bkp
        1 file(s) copied.

D:\DB2\Admin\ImageDDL>for /F "tokens=*" %a in ("test.sql") do call :ChgText "%a

D:\DB2\Admin\ImageDDL>call :ChgText "test.sql    *** want this to be a line from the file not the filename

D:\DB2\Admin\ImageDDL>set Text=test.sql

D:\DB2\Admin\ImageDDL>echo test.sql  1>>work.sql

D:\DB2\Admin\ImageDDL>exit /b

D:\DB2\Admin\ImageDDL>del "test.sql"

D:\DB2\Admin\ImageDDL>rename work.sql "test.sql"

D:\DB2\Admin\ImageDDL>exit /b


Report •

#13
March 9, 2010 at 09:47:33
For what it is worth . . .

This is on Windows 2003 Server Enterprise

The FORFILES command is also available in the Resource Kit for other Windows versions

The basic task at hand is simple string substitution (using variables) within ALL files that meet certain criteria (where FORFILES comes in).

This step is simply to preprocess files substituting tokens with environment values for that particular server before processing by the DB2 CLP.

I use sed or awk on unix to provide this function.


Report •

#14
March 9, 2010 at 09:51:55
Change:
for /f "tokens=*" %%a in (%1) do call :ChgText "%%a

To:
for /f "usebackq tokens=*" %%a in (%1) do call :ChgText "%%a"


Report •

#15
March 9, 2010 at 10:09:09
Worked !!!

I am speechless . . .


Report •

Ask Question