Friday, January 23, 2009

Numbered Output Redirection

Today my boss pointed out something to me which is kind of shocking even though you already know how stupid the command processor is.  Now we all know 2> is for redirecting stderr.  Simply > or 1> is stdout.  For stdin you use < or (less widely known) 0<.

But what if you do 3> or 4> or 5<?  Surely it does not interpret that as some special file handle?  Well, if you thought that, you’d be wrong.  Just like I was.  Try this:

echo Hi 3> out.txt

You’d expect to see Hi 3, wouldn’t you?  But nooooo, you’ll see Hi instead.

Where does the 3 go?  Well, the good interpreter thinks it’s a special file handle.  Crazy isn’t it?  But what if you really want to print Hi 3?  What do you do?  Thankfully the solution is easy and pretty obvious.

echo Hi 3 > out.txt

Yup, just put a space between 3 and >, and you’re good to go.

Sunday, November 16, 2008

Quotes In For Loop Set

One thing that just bit me the other day is how for /F loop treats a set enclosed in quotes as a literal string rather than a file name.  This is actually mentioned in the help for page, but still, this is something I did not anticipate.

Consider this example:

set FILEPATH=”c:\some dir with space\somefile.txt”

Now I wanted to go through the lines in that file, so naturally I tried to do this:

for /F %i in (%FILEPATH%) do (echo %i)

What do I get?  I get:

c:\some

Why?  Well, it turns out for /F treats anything in the set with quotes as a string instead of a filename.  OK, so let me try without the quotes then.

set FILEPATH=c:\some dir with space\somefile.txt

for /F %i in (%FILEPATH%) do (echo %i)

But now I get this error:

The system cannot find the file c:\some.

So it refuses to recognize the filename with spaces if I remove the quotes.  So what to do then?  The ugly way, unfortunately.

set FILEPATH=”c:\some dir with space\somefile.txt”

for /F “usebackq” %i in (`type %FILEPATH%`) do (echo %i)

Sigh…

Thursday, March 6, 2008

Setx Escape Character

I recently found out that there is something rather strange with how setx interprets character you pass to it when setting a variable.  It appears that setx has singled out \" as an escaped character.  So if you want to set variable MYVAR to have the value of My name is "Batcheero", here's how you do it:

setx MYVAR "My name is \"Batcheero\""

This is weird because I don't know of any other Windows commands that does this without specifically saying that it is taking regular expressions (like findstr /R).  And the other weird thing is, \" seems to be the only pair that it escapes.  If you try \' or \n or \\, it treats those verbatim.

The bad thing about this singled out treatment is, you have to be careful not to pass a value that ends with a backslash to setx.  For example, if you have a path of a directory that ends with a backslash, which is quite common, then you are in trouble.

set MYPATH=c:\program files\my app\

setx /M MYPATH "%MYPATH%"

This is going to be fine in the current context, but what gets set in global machine environment is c:\program files\my app" (notice the double quote in the end instead of backslash), which is definitely not what you want.

Just one more thing to worry about in Batch world.  Let me know if you are aware of other commands that has this kind of behavior.

Thursday, February 14, 2008

Set And Setx

Everybody knows to use set to set environment variable values.  And I guess everybody should know setx.  The crucial differences between set and setx are:

  • set takes effect in local cmd context.  Meaning once you exit or close the cmd window, you lose the environment variable.
  • setx takes effect in future cmd context.  So you won't see the environment variable and its value in the current cmd.  You need to open a new cmd window to see it.

This really leads to one obvious conclusion: always use set and setx side by side if you want to set something globally but you want to see it in effect immediately.

A good example is if you want to set an environment variable containing your current IP address.  You would do something like this:

ipconfig | findstr IPv4 > %TEMP%\ipaddress.out

REM This will give you something like this

REM IPv4 Address. . . . . . . . . . . : 192.168.1.3

REM You then parse the output file, taking the second token after splitting at the ":"

for /F "delims=: tokens=2" %i in (%TEMP%\ipaddress.out) do (

    set IPADDR=%i

)

REM Then remove the spaces

set IPADDR=%IPADDR: =%

REM Then to persist this in the system environment, you call setx.

setx /M IPADDR %IPADDR%

Now of course setx is much more powerful than simply calling it the way I did in the above example.  Take a look at setx /? to see the complete options.  For example the above usage can also be done using setx alone.

ipconfig | findstr IPv4 > %TEMP%\ipaddress.out

REM Then you use setx to read its value from a file, keying in on a string,

REM and get the value relative to that string. In this case we key in on the ":"

REM and get the token after that.  And setx coordinates start at 0, not 1.

setx /M IPADDR /F %TEMP%\ipaddress.out /R 0,1 ":"

The reason I hardly ever use this is because most of the time I need the environment variable defined in the current context first, and then also persisting it for future cmd contexts.   The second example above only does it for future context.

Monday, January 21, 2008

Creating Compressed Directory

Often times I find myself in need of creating a directory with compressed attribute turned on, so I can put some text log files in with less space consumption.  While md or mkdir doesn't support doing this directly, you can create the directory and later apply the compression attribute, so that any subsequent files copied to that directory will be compressed.

md SomeDir

compact /C SomeDir

copy SomeText.log SomeDir

That does it.  Not the most useful thing in the world.  But hey, you never know when you will need it.

Friday, January 18, 2008

Out Parameter In Function Call

Let's say you write a function or procedure or sub program or whatever you want to call it in batch.  How do you pass back a value to the caller?  You can of course set an environment variable, but that's like setting a global variable.  And that's not very nice if you need to call the function multiple times and only use the value after all function calls are made.

Consider this example:

@echo off

call :GETVALUE

set INPUT1=%USERINPUT%

call :GETVALUE

set INPUT2=%USERINPUT%

echo %INPUT1% %INPUT2%

goto :EOF

 

:GETVALUE

set /P USERINPUT=Input?

goto :EOF

Like I said, it's doable, but not very nice.  I find it more soothing to the eyes if I can pass the variable name as an argument to the function.  And here's how you do it:

@echo off

call :GETVALUE INPUT1

call :GETVALUE INPUT2

echo %INPUT1% %INPUT2%

goto :EOF

 

:GETVALUE

set /P %1=Input?

goto :EOF

Nice and simple.  All you need to do is remember that you can use %1 and other %<number> on the left hand side of an assignment.  And it makes you think for a second that you are programming a real scripting language.

Thursday, January 17, 2008

Escape Characters

I know this is going to be a confusing year, because my first post of this year is on a topic I don't really understand.

For some time now I have been confused what the correct method to escape certain characters from being interpreted when I try to print them out.  Let's say I want to print this line:

< & >

I can't just say

echo < & >

Because I'll get an error that way (try it yourself if you don't believe me).  I have to escape those special characters.  To do that I can use the caret (^).

echo ^< ^& ^>

Nice and easy.  But then if I want to print this line:

%SYSTEMDRIVE%

The batch interpreter does something really baffling.  If I'm doing it from command line, I can escape it with a caret like other special characters.

echo ^%SYSTEMDRIVE^%

But it I do it from inside a batch file, that no longer works.  I have to escape the % not with a caret, but with another %.

echo %%SYSTEMDRIVE%

Confuse the hell out of me, I tell you.  So now I live by these rules:

  1. Always escape special characters using a caret in front of the special character.
  2. Except when you are in a batch file and need to escape a %, then use a %%.

I hope that helps some of you.  Oh wait, nobody reads this blog but me.  Oh, well.