Welcome

My name is Jason and I am a software developer in the Bay Area. For fun I like to hike, run, shoot some photos, and take advantage of the many other activities California state has to offer. To the right you will see my resume.

Recent Books
  • Head First Design Patterns
    Head First Design Patterns
    by Elisabeth Freeman, Eric Freeman, Bert Bates, Kathy Sierra, Elisabeth Robson

Entries in quick fix (2)

Sunday
Apr212013

Quick Fix: File count of a directory

Recently I had put together a DOS batch script that would monitor the number of PDFs in a directory. Once that count got to or above 500 it would then turn on some services to process the PDFs. Now there's probably numerous ways to determine the number of files in a given path, but I wanted to do it in a single line and ideally exercise existing DOS functionality where possible. That's how I came up with the following single line command chain.

for /f "tokens=1" %%A in ('dir %pdfDir%\*.pdf 2^>nul ^| find /i "File(s)" ^|^| echo 0') do (set fileCount=%%A)

First, let me explain why all the carrots “^” are present. If you didn't know, this is how you escape special characters, like pipes “|” and redirects “>”  in DOS. Since we're using the FOR command, we want to escape these characters because the FOR command will take the entire string given and run it internally to process the output. Before this happens though DOS will read the entire line, process/parse any variables and/or special characters, like pipes and redirects, and then run the command. This parsing can get confused when it sees the pipes and redirects without carrots and thinks you'll want to execute these actions before passing them to the FOR command. This is the same as to why you have to use double percent signs “%%” in a script for the FOR command, but not when using it on the command line. The double percent signs is how you escape percent signs so the DOS batch file doesn't treat it like a variable it should process, but let's the FOR command process it instead.

C:\TechBlog>echo Redirect = ^>  Pipe = ^|  Double pipe = ^|^|
Redirect = >  Pipe = |  Double pipe = ||

If you don't understand the “2>nul” syntax, please see my earlier post “Redirection of the output streams in DOS scripts”.

So let's break down what's happening here. I'm going to simplify the full command to the following to help explain what's going on and later describe why the “echo 0” is present.

for /f "tokens=1" %%A in ('dir %pdfDir%\*.pdf 2^>nul ^| find /i "File(s)"') do (set fileCount=%%A)

When you do a DIR on a directory you get output like the following.

C:\TechBlog>dir
 Volume in drive C has no label.
 Volume Serial Number is A48F-FE2B

 Directory of C:\TechBlog

04/21/2013  12:00 PM    <DIR>          .
04/21/2013  12:00 PM    <DIR>          ..
04/21/2013  12:00 PM               391 PdfFile1.pdf
04/21/2013  12:00 PM               444 PdfFile2.pdf
04/21/2013  12:00 PM               338 TextFile.txt
               3 File(s)          1,173 bytes
               2 Dir(s)  16,957,591,552 bytes free

Most of this information we don't care about except for the line stating the file count. We want to isolate this line so that we can use the FOR command to parse it. To do this we pipe the output of the DIR into the FIND command. The FIND is setup to only return lines that have the text “File(s)” in them. This line of text is returned and the FOR command parses it. Because we're using the FOR command with “tokens=1” we're saying that after the FOR command parses the line, which by default is by whitespaces, we only want the first token found. That token will be the number of files. So now when the FOR command runs the command (set fileCount=%%A) it's setting fileCount to the number of files found in the directory. We have our file count in a variable and ready to use!

Ah, but there's a catch. There's always a catch. Sadly when you do a DIR on something specific, like “dir *.doc” and there are no DOCs present it doesn't simply return “0 File(s)”, but the message “File Not Found” which isn't really useful for what we're trying to accomplish. We need some error handling.

C:\TechBlog>dir *.doc
 Volume in drive C has no label.
 Volume Serial Number is A48F-FE2B

 Directory of C:\TechBlog

File Not Found

This is where the “echo 0” comes in. Going back to the original full command, when DIR doesn't pipe any text into FIND that contains “File(s)”, the FIND command returns a failed (non-zero) exit code. Adding “||” to the end of a command means only run the following command if the previous has failed. Since FIND has failed, the “echo 0” command is ran returning the text “0” which FOR then parse. This then resolves our “File Not Found” scenario and we're good to go!

Do you know of another single line command/command chain to determine the number of files in a path? If so, share it! I'd love to see how others have accomplished this.

Sunday
Dec112011

Quick Fix: “Invalid number” error in DOS batch script

I ran into this the other day. After a little searching around I found the answer and thought I'd share the way I resolved it. Hopefully this will shorten someone else's search time.

The error message I was getting was the following: 

Invalid number. Numeric constants are either decimal (17),
hexadecimal (0x11), or octal (021).

It turns out this is happening on the script line that's doing some math with the “set /a” command. It could be happening in other commands as well, but this is where I've seen it.

So here's what you need to know to resolve this. As the error message indicates, it treats values that have leading 0's as octal. Each digit of a octal value is represented with the numbers 0-7. For example octal value 022 is the same as decimal value 18. Why you're getting the error message is because you have a value that has a leading zero, indicating an octal value, but contains at least a digit that is either an 8 or 9. Neither number conforms with how an octal value is represented and this is the source of the error.

Here's the quick solution I came up with. Since I was dealing with dates (months and nth day of the month) I knew that I would have values that are at most 2 digits long and always positive. So before doing any arithmetic on these values I would do a quick check for a leading zero and remove it if present. 

@echo off
(set month=08)
echo The month should be 08: %month%
 
REM The error message should appear. Bummer!
(set /a nextMonth=%month% + 1)
echo The next month should be 9: %nextMonth%
 
REM Checking for leading zero and removing it if present.
if "%month:~0,1%"=="0" (set month=%month:~1%)
echo The month should now be 8: %month%
 
REM No error message. Hooray!
(set /a nextMonth=%month% + 1)
echo The next month should be 9: %nextMonth% 

To break it down, what's happening in the first part of the IF statement is we're obtaining a substring from month whose index starts at 0 and we want the substring to be 1 character long. This means that the code “%month:~0,1%” is just returning the first character of month, which in this case is a zero. Because the IF statement is true, we now set the value of month so that it equals the substring starting from the index of 1 and then all following characters. This means that “%month:~1%” will return the 8. And now the leading zero has been removed and we're good to go!

If you know you're values are only going to be 2 digits long, you could get away with an IF statement like the following:

if %month% lss 10 (set month=%month:~-1%)

I prefer the substring method I initially showed because you could later add some looping logic for removing multiple leading zeros if you were dealing with values like 00789.

Remember, this solution does not handle negative values or values that have more than 1 leading zero. Please take that into consideration when implementing this workaround in your script.

I hope this helps unblock you and clears up any issues you may have been having. If you have another way you handle related scenarios, post it in the comments. I'd love you hear other ways to accomplish this!