Why That Batch For Loop Isn’t Working

Time for another fun foray into Windows batch scripts. Perhaps you've used the FOR /F command to loop through the contents of a file (for instance, perhaps some data that was redirected to a text file from a command). Grab a line, act on its values, and output some text and commands.

Let's set this up. First, we have a data file named SomeAccounts.txt:

Josh
Mary
Suzy
Amanda
Trisha
Ben

Then, we have ProcessAccounts.bat, which we want to just loop through the accounts in the text file, tell us what they are, and tell us the first letter of the account name (just to have something to do):

set file=SomeAccounts.txt
FOR /F %%i IN (%file%) DO (
set username=%%i
echo My account, %username%, starts with %username:~0,1%.
)

Except when you do this, you encounter a problem: All of the values from the FOR loop are the same! It's as if the for loop ran the proper number of times, but it just ran on the last record over and over again! See below:

My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.
My account, Ben, starts with B.

What's actually happening is the FOR loop is indeed running over every line, and setting the variables as instructed, but the results of those variables being altered isn't echoed until the FOR loop is complete, so the last value of the variable is what displays. This wouldn't be a problem if you were just using your FOR parameter, in this case %%i, but any variables you set while in the FOR loop, like username, experience this "wait until you're out of the loop" phenomenon.

The fix is simple enough, if you know about it! But I've found the solution to be a bit elusive, which is the whole point of sharing it now.

The key is the setlocal EnableDelayedExpansion command. As explained at ss64.com, making this statement before your FOR loop will enable you to display variables as their value at the moment you're referencing them, or their "intermediate values" while in the middle of the FOR loop. In addition to calling the setlocal command, you then have to reference your variables with the exclamation point (!) rather than percent (%) to indicate that you want to use the intermediate value.

Your script will then look like this:

setlocal EnableDelayedExpansion
set file=SomeAccounts.txt
FOR /F %%i IN (%file%) DO (
set username=%%i
echo My account, !username!, starts with !username:~0,1!.
)

It will now happily act as desired, outputting these results:

My account, Josh, starts with J.
My account, Mary, starts with M.
My account, Suzy, starts with S.
My account, Amanda, starts with A.
My account, Trisha, starts with T.
My account, Ben, starts with B.

Upgrading Subversion Requires a Bindings Update for Trac!

My Subversion/Trac server was at Trac v0.9.6 and Subversion v1.3.x because those were the latest stable releases when I set up the server. I decided it would be relatively quick and painless to at least get the latest version of Subversion (v1.4.5) installed since I didn't see anything on the web about Trac v0.9.6 being incompatible with newer Subversion builds.

Using the Windows binary installer, I had no problem installing Subversion v1.4.5 on the server. I tested everything and Subversion still worked, it showed the new version when accessing via web access, and Trac still worked fine.

Alas: Don't forget that an upgraded version of Subversion will not upgrade your repository. It will upgrade a working copy of a checked-out repository, but it will not upgrade the repository itself.

That said, I was unaware of one more step that you must take to upgrade Subversion on a Subversion/Trac setup: You must also upgrade the Python bindings to Subversion.

This became apparent the next time I created a new repository, which was not a v1.4.x repository, and when I tried to build a Trac environment to point to it, Trac got upset because of the classic "Expected version '3' of repository; found version '5'" error. To fix this, you must set up new bindings to the new version of Subversion, as explained in the TracSubversion page.

Now, I obviously love Subversion and I love Trac, but honestly, straight-forward documentation that is easy to understand for someone who doesn't want to get in the thick of it isn't really the strong suit for these communities, at least when it comes to installation and deployment on the server. What exactly it means--and how to do it--when they say, "Update the Subversion bindings" is not easy to understand. However, the solution is simple. All that is needed is to download the appropriate "svn-python" Windows installer that matches your version of Subversion and Python (in my case, 1.4.5 and py2.3) and run it on the server.

In my case, I had to stop Apache for the installation to succeed. Upon restarting Apache, everything worked great.

Windows Vista and Samba Not Getting Along: NTLMv2 is the Culprit

After installing Windows Vista, I could not connect to my Samba fileshares. I'm running Samba v3.0.10 on CentOS v4.4.

It turns out that NTLMv2, the authentication protocol, is required by default on Windows Vista. According to the Samba Features by Release wiki page, support for NTLMv2 in Samba wasn't fully developed until Samba v3.0.21.

Running yum would be a quick way to upgrade Samba to a more recent release. For some reason, though, the repositories I'm pointing to only have v3.0.10 as the latest available update. Rather than hassling with it, I found an article that attacks the issue from the Vista end.

The article Get Vista and Samba to Work explains how to get Vista to use the older authentication protocols, like the original NTLM. After making this change, I was able to login to my shares immediately.

Basically, all you need to do is run the Local Security Policies console snapin (secpol.msc), open Local Policies --> Security Options --> Network Security: LAN Manager authentication level, and change the setting from "NTLMv2 responses only" to one of the more lenient settings, like "LM and NTLM – use NTLMV2 session security if negotiated".

This works for me because I have one, sometimes two, machines with Windows Vista connecting to my server. If you had lots of machines connecting to the server, it'd obviously be worth your time to just upgrade Samba to a version that supports NTLMv2.

Free Command-Line Zip on Windows

Both Linux and Mac OS X have zip, gzip, and bzip2 command-line tools. What about Windows? If you're trying to do some scripting to automate some archiving or backup, and you want it to be a classic, WinZip-compatible .zip file, how can you do it?

WinZip offers a WinZip Command Line Add-on free of charge--if you already own a copy of WinZip Pro!

You shouldn't have to pay for command-line zip. And you don't have to. Enter Info-ZIP. This workgroup has been maintaining free, portable, high-quality versions of zip and unzip. They have plenty of command-line arguments like you would expect from an open source project.

So, with this project's executables in your system path, you can write up a batch file that is executed as a Windows scheduled task. Maybe something like this:

zip -q -S -r c:pathMyBackup.zip c:data -i@include.lst

This will zip the c:data directory. Arguments: -q to do it quietly, -S to include system files, -r to recurse into subdirectories. Finally, use -i to point to a file that indicates the exact files to include, by means of a carriage return delimited list.

You can alternatively use -x to specify only which files should be excluded. Perhaps something like this:

zip -q -S -r c:pathMyBackup.zip c:data -x@exclude.lst

The command-line flags are all optional, of course. This tool is certainly a must-have for the Windows scripter.

  Theme Brought to you by Directory Journal and Elegant Directory.