Using Java Classes in Windows Batch Files Blog

Version 2



    Basics of the Command Line
    Implementing Console-Based Programs in Java
    Files and File Types
    The TKJavaRunner Application
    Some Examples
    Limitations of TKJavaRunner

    Although Java is an ideal language for implementing rich GUI applications, it is equally well-suited for the development of small console-based programs that, in turn, are predestined to be put together in shell scripts or batch files. In this article, I will discuss how to integrate Java classes and packages into Windows batch files.


    Although graphical user interfaces of today's operating systems provide elegant means to control the machine and to initiate maintenance tasks, power users and system administrators know and appreciate the benefits of the command line. Small tools can be put together in so-called shell scripts or batch files(the terminology varies among platforms, but all essentially means the same thing) to achieve things that are impossible, or at least impractical, with a GUI.

    This is especially true for Linux, Mac OS X, and other Unix-like operating systems, which provide particularly powerful command-line interpreters (commonly referred to as shells). In principle, this is true for Windows as well. From its beginnings, DOS had a component named and it is still present (in quite an enhanced version, by the way) in current versions of Windows as cmd.exe. Though it is far not as powerful as, for instance, the Unix bash shell, it serves as a solid base for launching maintenance tasks, administrating the machine, etc. Unix-like operating systems are famous for their rich sets of command-line-centric tools. Unfortunately, the number of such programs that are shipped with Windows is much smaller.

    But if a required tool is not at hand, why not implement it on your own? That's how many of the above-mentioned Unix tools came into existence, after all.

    Basics of the Command Line

    Before we get into Java, let us consider some basic techniques and concepts: how do console-based programs work, in general? In Unix environments, they used to be written in the C or C++ programming language, and thus are native programs. To be easily accessible, they are usually put in a common directory that is mentioned in the PATH environment variable, so that the shell can find then.

    Furthermore, these programs make use of a concept calledchannels, three of which are particularly important: the standard input channel, the standard output channel, and the standard error channel. Programs get input from the first, send output to the second, and use the third to provide information if an error has occurred. What physical media these channels are connected to is not specified -- the program does not know, and doesn't need to know. It may receive input from the console a user is sitting at the computer or from a file. The same is true for output, which need not be a screen; it may be a printer or a networked file.

    Another important concept is that programs can be passed arguments that they can query as they start up. When they terminate, console-based programs can provide feedback if everything went fine during execution. This is done using a so-called exit status, which is typically zero if nothing unexpected happened.

    All of the above traits are true for Windows, as well. Therefore, we can make use of these concepts in console-based programs, which we in turn can tie together in batch files. As I have already mentioned, in Unix-like environments command-line tools usually reside in a few common directories that are all mentioned in the PATH environment variable. This is necessary for the shell to find them (when no absolute path is specified). This applies to cmd.exe as well, so we should create a directory in which we put all programs that we want to use inside of batch files. Although the Windows directory is part of the search path by default, I would advise you to not put your command-line tools there, since that would be mixing system and user files. Instead, create your own bin folder in Program Files and extend your PATH to include that directory.

    Implementing Console-Based Programs in Java

    Fortunately, Java resembles C and C++ in many aspects. In particular, Java programs meet all of the requirements of console-based applications. To repeat, these are:

    • The ability to access the standard input, standard output, and standard error channels.
    • The ability to access the arguments that were passed upon startup.
    • The ability to provide a result (i.e., an exit code).

    Taking this into account, Java seems a natural choice for console-based applications. There is one important drawback, though. Since the standard Java compiler (javac) produces byte code, there is no .exe file that we can put into our bin folder. The usual way to run a Java application from the command-line prompt is to invoke the Java runtime, passing it the name of the class containing themain method or a .jar file (with a manifest file that mentions where main is) to launch, plus additional arguments that will be passed to the program. Though this is perfectly OK for program-launcher scripts, it is inconvenient when writing shell scripts, since the preferred approach is to simply write a program name and pass arguments to it. In our case, this would be the class name. To solve this, we have two options:

    • Make them (or at least a part of them) native executables.
    • Make Windows think they are native.

    Which approach is best for you depends on what you wish to do with your programs. There are many tools available that can transform classes into native .exe files or put wrappers around them. For example, recently featured an interesting series of articles by Joshua Marinacci about how to Make Your Swing App Go Native. In this second part of the series, Joshua introduced a commercial tool called JexePack that creates native executables with unique program icons. Such wrapper programs actually launch the Java interpreter, which in turn runs the Java program. This is certainly great if your programs have to look professional; for instance, because you plan to distribute them. So theoretically, we might use this tool for our purposes as well, but this implies that for each Java class we plan to use in a batch file, we need to create an appropriate native wrapper.

    Another (at least theoretical) possibility is to use the GNU Compiler for the Java Programming Language, which can create native binaries. However, installing and using the GNU Compiler Collection on a Windows-based machine is far beyond the scope of this article. So let us analyze the second approach.

    Files and File Types

    What we are trying to do is make Windows think that .class files are (or at least behave like) native executables. If you double click on a .html file, your browser is launched and displays the file. The same is true for sound files, graphics files, and so on: files of a certain type can be assigned an application that is used to render, display, or otherwise present the contents of the file. Opening those files works within cmd.exe, too. To try, launch cmd.exe and navigate to a directory that contains a .html file, for example. Then type the name of that file including its extension, and hit Enter. The browser will show your file. If we assign .class files to the Java runtime system (java.exe) and put the class files in our binfolder (the one I suggested creating above, so you can put your class files there and not mix them up with system files), we can access them from batch files by writing<class>.class. At the moment, we cannot omit the extension, because this is allowed for executables only. We will see how to fix that later. We do have a greater problem, though.

    java.exe does not expect filenames, but class names. If you double-click on a file, its name and path are passed to the application. Sadly, this is not what java.exeneeds. However, an application that takes the filename of the class to launch, extracts the classname, and passes that tojava.exe should do the trick.

    The TKJavaRunner Application

    What would such a class launcher look like? Below we have a trivial implementation called TKJavaRunner. For your convenience, I have provided an archive with all listings, class files, and executables in the Resources section below. To compileTKJavaRunner.exe I used the excellent lcc-win32 IDE and compiler package.

    /********************************* * * * TKJavaRunner * * * * This program invokes the Java * * runtime with a class name * * which is built from the first * * argument; further arguments * * are passed to the Java * * application * * * *********************************/ #include <stddef.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #define MAXLEN 256 int main(int argc, char **argv) { char *path; char *classname; char *extension; int result = 0; char cmd[MAXLEN]; int i, len; if (argc < 2) { fprintf(stderr, "TKJavaRunner [.class file} [argument1] [argument...]\n"); result = 1; } else { path = argv[1]; classname = strrchr(path, '\\'); if (classname == NULL) classname = path; else *(classname++) = 0; extension = strrchr(classname, '.'); if (extension == NULL) result = 1; else { *(extension++) = 0; sprintf(cmd, "java -cp \"%s\" %s", path, classname); len = strlen(cmd); for (i = 2; i < argc; i++) { len += (strlen(argv[i]) + 1); if (len >= MAXLEN) break; strcat(cmd, " "); strcat(cmd, argv[i]); } // fprintf(stderr, "%s\n", cmd); result = system(cmd); } } return result; }

    Let us have a look at the program. It expects at least one argument, the complete path and name of the class file to launch. Additional arguments will be passed to the class upon startup of the Java virtual machine. Since java.exe expects a class name, we remove the path and the extension. Then a new command line is constructed that contains the name of the Java runtime launcher (java.exe) with several arguments:

    • The -cp option to add the directory that contains our class file to the class path.
    • The class name without its extension.
    • Any additional arguments.

    The program assumes that Windows knows where to findjava.exe; its directory therefore has to be mentioned in PATH. If you work with Java at the command-line prompt now and then, this is quite a good idea anyway. But how do we use TKJavaRunner? We need to assign .class files to it. Though the specifics may vary depending on your version of Windows, the basic steps are as follows. First, open the Folder Options dialog, as shown in Figure 1.

    Figure 1
    Figure 1. Opening the Folder Options dialog

    After that, check to see if .class is already a registered file type. If this is the case, you will find an entry similar to the one shown in Figure 2. If not, create an entry by selecting the New button.

    Figure 2
    Figure 2. Browsing the list of known file types

    Once you have made sure .class is a registered file type, hit the Advanced button to check the settings. Please note that in Figure 3 we pass up to nine arguments to the program. Since%1 refers to the class file itself, we need to make sure that additional arguments are passed to our program. TKJavaRunner is a simple console-based program; therefore, the other settings in this dialog box do not apply.

    Figure 3
    Figure 3. Configuring the .class association

    There is one more problem we need to fix. Since we want our classes to behave as native executables, we want to be able to launch them without their .class extensions. Windows maintains a list of extensions to be treated as executables. To modify this list, right-click your My Computer icon on the desktop and select Properties from the contextual menu. A dialog box will appear. Move to the Advanced tab. You will notice a button labelled Environment. If you select that, another dialog box will pop up. As you can see in Figure 4, there is a variable called PATHEXT, which contains a semicolon-delimited list of extensions of executable files. We just need to add .class to this list. After that we can run our classes just like native executables.

    Figure 4
    Figure 4. Modifying the PATHEXT variable

    Some Examples

    Now that we have configured Windows to recognize .class files, let us do some testing with cmd.exe. I have written two sample programs, named FileSize and Sum. You can get the source and class files in the Resources section at the bottom of this article. Put at least the class files to your previously createdbin folder, so that cmd.exe can find them.

    FileSize expects filenames as its arguments. For each given filename, it prints the size of the file to the standard output. Sum does not take arguments, but reads lines from standard input. Each line is treated as a decimal number.Sum sums all numbers it has read up until an end of file signal is received. It then prints the result to standard output.

    Neither of these programs is particularly useful on its own, but if we combine them, we can, for example, compute the size of a certain directory. Within cmd.exe type FileSize * > sizes.txt and hit Enter. You can check the result by executing type sizes.txt. After that, type Sum < sizes.txt. What happens here is thatcmd.exe expands * to a list of all file (and directory) names in the current directory. These names are passed to FileSize, which prints the sizes of the files. Since we redirected the standard output ofFileSize, we did not see anything on screen. The values were written to a file named sizes.txt instead. WithSum we do the opposite; we force it not to read from the keyboard but from the previously created file. We could write a small batch file that takes one argument, the pathname of a directory, and computes the sum of all file sizes within this directory.

    Limitations of TKJavaRunner

    So far, TKJavaRunner seems to do a good job. However, it has some significant drawbacks, which I am going to cover in this section. The first one is obvious: it does not handle .jar files. This is a minor issue, since .jar files usually contain more complex, GUI-based applications. On the other hand, the Java runtime usually registers .jar as a file type, so if this .jar archive contains a manifest file that specifies the class containing the main method, it can be launched with a double click. And if we add .jar to the PATHEXT environment variable, we can run the archive from cmd.exe as well.

    Another minor drawback is that Windows launches TKJavaRunner, which in turn starts the Java runtime. We might consider this a performance issue, since it takes place each time a Java class has to be launched (from cmd.exe). My assumption is, however, that we can ignore the time needed to launch TKJavaRunner, especially when compared to the time needed to bring up the Java runtime.

    The biggest drawback is that TKJavaRunner cannot handle classes that contain a package statement. In order to find packaged classes, the virtual machine must know the base directory of the package, not the directory that contains the class. But that is exactly what TKJavaRunner does. It determines the path of the class and adds that directory to the classpath. To illustrate this, imagine you have put Sum.class into C:\bin. If you run it from cmd.exe, C:\bin\Sum.class is passed to TKJavaRunner, which sets the classpath to C:\bin using the -cp option and determines Sum as the class name. Since Sum has no package statement, TKJavaRunner is right with its assumption. However, if we assign it to the package com.oreilly.tkuenneth, the class file should be in a directory named some path\com\oreilly\kuenneth. To work correctly, TKJavaRunner would need to add some path to the classpath and determine com.oreilly.tkuenneth.Sum as the (fully qualified) class name, which it, as we have already seen, does not.

    The fact that packages produce a directory tree presents another problem. Remember that cmd.exe finds class files because we added a bin folder to PATH. To find a class nested deep inside of a package hierarchy, we would need to add each directory that is part of this tree to PATH. This is not practical, for the following reasons:

    • PATH becomes difficult to maintain.
    • Each time a filename has to be resolved, all PATHentries are scanned. To a certain extent, this might affect the performance of your machine.

    Consequently, it seems reasonable not to package the main class of programs you wish to access from the command line. Of course, they may reference classes of other packages if the Java runtime knows the path of the package base directory. This, for example, applies to Java extensions, which are put in the extdirectory. This is particularly convenient, since you can put any .jar file there. Another option is to modify TKJavaRunner to add another directory to the classpath using the -cpoption.


    Java provides everything necessary to write console-based programs. Since class files contain byte code, they cannot be invoked directly. Instead, the Java runtime has to be launched first. To mimic the behavior of native executables in Windows, we have used file associations and an appropriate wrapper program. This allows us to treat class files almost as .exefiles. As I have mentioned earlier, the shell plays a very important role in Unix-like environments. Therefore it seems desirable to use Java classes in shell scripts just like native executables in these environments, too. Unfortunately shells do not know file associations, so there is no common approach we could use for all Unix-like operating systems. For Linux, there is a kernel module called binfmt_misc, which adds support for the execution of non-native binary formats, which is based on amagic number or a file extension. This mechanism is similar to our approach using a wrapper program. A file that contains a magic number or has an appropriate extension is passed to its corresponding launcher program. I hope to discuss this Linux feature in a later article.

    In Windows, the approach of using a launcher program does not necessarily require a native program. What we could do instead is assign .class files to java.exe with a command line as follows: java.exe TKClassLauncher "%1" "%2" ... "%9". The TKClassLauncher class would act as our launcher program, so it determines the class to be started, loads it, and invokes its main method. Doing so has several advantages:

    • We need no extra time to launch a native wrapper program; instead, the Java runtime is brought up immediately.
    • We could even solve the problem regarding packages by implementing an appropriate ClassLoader that does not depend upon a previously defined classpath.

    Since dealing with the ClassLoader class is an interesting topic, it might be worth a further article. In any case, I greatly appreciate your feedback.


    Source code and binaries for TKJavaRunner, FileSize, and Sum