Batch file to create files using a file containing file names
February 28, 2011 3:38 PM   Subscribe

Windows batch file help: I have a file containing a list of files, I want to generate a new file for each of the files in the list.

I have filenames.txt, which contains a list of file names with no extension, i.e.:

file1
file2
file3

I need a batch file that will create a .abc file for each of these files and contains data that also includes the file name:

[Setting1]
Value1=2.0
[Setting2]
SrcFile=C:\some files\file1.xyz
HostName=MyHost
[Setting3]
...

I found something close on a forum and modified it to:

@echo off
del C:\some files\*.abc
for /f "tokens=* delims=" %%a in (filenames.txt) do (
echo [Setting1] >>.\%%a.abc
echo Value1=2.0 >>.\%%a.abc
echo [Setting2] >>.\%%a.abc
echo SrctFile=C:\some files\file1.xyz >>.\%%a.abc
echo HostName=MyHost >>.\%%a.abc
echo [Setting3] >>.\%%a.abc
...
)

There are about 85 lines I want to output to each of the 275 files I'm creating. The problem I'm running into is when I have more than five or so ECHOs I get the error, "The process cannot access the file because it is being used by another process" when running the batch file.

What am I doing wrong? Also, I do have Python installed on my PC, if this is best handled in that environment (even though I know almost nothing about Python). Thanks!
posted by DakotaPaul to Computers & Internet (10 answers total) 4 users marked this as a favorite
 
Best answer: Python is the way to go.

MeFi likes to mangle Python source, but here goes:
template = """
[Setting1]
Value1=2.0
[Setting2]
SrcFile=C:\some files\%(NAME)s.xyz
HostName=MyHost
[Setting3]
"""

filelist = "filenames.txt"
extension = ".abc"

names = open(filelist).readlines()
names = [name.strip() for name in names]

for name in names:
    if name:
        print "Creating:", name+extension
        open(name+extension, "wb").write(template % {'NAME': name})

posted by and for no one at 3:56 PM on February 28, 2011


Best answer: er, you probably want
    open(name+extension, "w")
instead of
    open(name+extension, "wb")

posted by and for no one at 4:02 PM on February 28, 2011


Response by poster: Thank you, and for no one, that did the trick!

Man, I really gotta learn Python.
posted by DakotaPaul at 4:08 PM on February 28, 2011


I can't recommend Python enough. Good luck.
posted by and for no one at 4:15 PM on February 28, 2011


Best answer: It's actually quite doable - concise even - in cmd script. This works:
set dir=C:\Some files
set ext=abc
del /f /q "%dir%\*.%ext%" >nul
for /f "delims=" %%f in (filenames.txt) do >"%dir%\%%~f.%ext%" (
	echo.[Setting1]
	echo.Value1=2.0
	echo.[Setting2]
	echo.SrcFile=%dir%\%%~f.%ext%
	echo.HostName=MyHost
	echo.[Setting3]
	echo....
)
Subtleties:

Redirecting the del command's output to nul suppresses complaints if there are no files to delete.

There's no need for "tokens=*" in the for /f args, because with "delims=" there can be only one token anyway.

Using %%~f instead of %%f strips any surrounding double-quotes that may occur in the expansion of %%f. Not strictly necessary in this application, but generally good practice.

Using a single write redirection to cover the entire group of echo statements instead of individual append redirections on each echo is clearer and less error-prone.

Using echo. means you'll have no trouble if you need to echo a blank line.

Any time you need a ) in your output files, you'll need to write that as ^) inside the appropriate echo statement; without that, the ) will be taken as terminating the statement group controlled by the for command.
posted by flabdablet at 5:10 PM on February 28, 2011


Response by poster: Thanks for the cmd script knowledge, flabdablet! My next problem is making multiple copies of the same files, i.e:

file1(part01).abc
file1(part02).abc
file1(part03).abc
file2(part01).abc
file2(part02).abc
file2(part03).abc
file3(part01).abc
file3(part02).abc
file3(part03).abc
...

Which contain the data:

Setting1]
Value1=2.0
[Setting2]
SrcFile=C:\some files\file1(part01).xyz
HostName=MyHost
[Setting3]
...

Setting1]
Value1=2.0
[Setting2]
SrcFile=C:\some files\file1(part02).xyz
HostName=MyHost
[Setting3]
...

All of which is still only based on the original filenames.txt.
posted by DakotaPaul at 9:44 PM on February 28, 2011


This works:
set dir=C:\Some files
set ext=abc
del /f /q "%dir%\*.%ext%" >nul
for /f "delims=" %%f in (filenames.txt) do (
	for /l %%n in (1,1,3) do >"%dir%\%%~f(part0%%n).%ext%" (
		echo.[Setting1]
		echo.Value1=2.0
		echo.[Setting2]
		echo.SrcFile=%dir%\%%~f(part0%%n^).%ext%
		echo.HostName=MyHost
		echo.[Setting3]
		echo....
	)
)
Extra-subtle subtlety: even though the redirection >"%dir%\%%~f(part0%%n).%ext%" is contained within the parentheses associated with the outer for loop, there is no ^ required before its embedded ). This may be because of the double-quotes, or because redirections are processed before anything else; not sure.

Also, horrible kludges are going to be necessary if you need %%n greater than 9, because cmd's string handling is abysmally clumsy. It can still be made to work, but don't try doing so without a safety net. And a vomit bucket.
posted by flabdablet at 11:45 PM on February 28, 2011


Here's an example that generates names with (part00) through (part15) embedded in them:
setlocal enabledelayedexpansion

set dir=%temp%\Some files
set ext=abc
del /f /q "%dir%\*.%ext%" >nul

for /f "delims=" %%f in (filenames.txt) do (
	for /l %%n in (100,1,115) do (
		set num=%%n
		set name=%dir%\%%~f(part!num:~1!^).%ext%
		>"!name!" (
			echo.[Setting1]
			echo.Value1=2.0
			echo.[Setting2]
			echo.SrcFile=!name!
			echo.HostName=MyHost
			echo.[Setting3]
			echo....
		)
	)
)
The key thing here is that the inner loop runs not from 0 to 15 but from 100 to 115. That means that set num=%%n will always make num a three-digit string; the construct !num:~1! then drops the first character, resulting in the desired two-digit string between 00 and 15.

The other necessary piece of nasty here is the use of setlocal enabledelayedexpansion to make the !variable! construct available. This works just like %variable% except that it happens just before command invocation instead of early in the parsing process. Using %num% and %name% instead of !num! and !name! would result in their values being expanded during the parsing of the outer for loop, which would make the set num and set name commands inside the inner loop useless.

You can see the difference if you run this thing with echo left on: where %dir% and %ext% appear in the script you'll see their expanded values echoed to the cmd window every time, while !num! and !name! echo as themselves even though their expanded values do get used to build the output filenames and content.

Doing this in Python would probably involve fewer test iterations and rather less swearing and nausea. I think it's only an unwillingness to admit defeat that keeps me writing things with cmd.
posted by flabdablet at 12:51 AM on March 1, 2011


By the way, it is the double-quotes that make ^) unnecessary inside the previous version's redirection.
posted by flabdablet at 12:54 AM on March 1, 2011


And now I see I've missed the fact that you wanted .abc extensions on the generated files but .xyz extensions on the SrcFile= lines inside them. So just get rid of my %ext% variable and do it like this:
setlocal enabledelayedexpansion

set dir=C:\Some files
del /f /q "%dir%\*.abc" >nul

for /f "delims=" %%f in (filenames.txt) do (
	for /l %%n in (100,1,115) do (
		set num=%%n
		set name=%dir%\%%~f(part!num:~1!^)
		>"!name!.abc" (
			echo.[Setting1]
			echo.Value1=2.0
			echo.[Setting2]
			echo.SrcFile=!name!.xyz
			echo.HostName=MyHost
			echo.[Setting3]
			echo....
		)
	)
)
Also, because all the file content gets generated using a write-redirect rather than an append-redirect, you might not actually need the del command.
posted by flabdablet at 1:05 AM on March 1, 2011


« Older Photo processing / Flickr toolchain for Mac?   |   A friend in need Newer »
This thread is closed to new comments.