Executing System Commands with ColdFusion

Update 13 May 2008: This project has been moved over to RIA Forge at http://systemcommand.riaforge.org/

This is an update to a previous entry.

Typically when you want to execute a system command you would use the <cfexecute> tag, but there are some situations when this may not be ideal such as when you want to capture both the "standard output" and "error output" streams of the system command.

Standard Output Stream and Error Output Stream

Many command line programs send output to two data 'streams':
a) Standard output stream
b) Error output stream

When you manually execute a program the command line information sent to either stream is simply displayed on the screen - there is no distinction which stream is being used.

Example

For example, suppose we execute the command to determine the version of java running on your system. You may execute the following command:

java -version

Which on my machine results in:

java version "1.6.0_03"
Java(TM) SE Runtime Environment (build 1.6.0_03-b05)
Java HotSpot(TM) Client VM (build 1.6.0_03-b05, mixed mode)

The problem with <cfexecute>

Suppose you execute the command above using using:

<cfexecute name="java" arguments="-version" timeout="1" variable="output" />
<cfoutput>#output#</cfoutput>

This produces no output!

What happened? Well, cfexecute can only capture output sent to the standard output stream, but not anything sent to the error output stream.

In this example, the java -version command sends all of its output to the error output stream (which seems a bit odd, but it came in useful for this example).

So how can we capture both the standard output and the error output streams?

Using a batch file wrapper

One method of capturing both streams is to put the command inside a batch file and send the two output streams to two files, then read them back in again:

Suppose we have a file javaversion.bat with the content

java -version

Then we run the batch file (on a windows machine) as follows

javaversion.bat 1>stdout.txt 2>errout.txt

This is just the syntax for capturing the standard output (stream 1) and error output (stream 2)

This will result in the creation of two files:
stdout.txt which has no data.
errout.txt which has the version data.

The next step would be to read in the files just created to get the output data.

Using Java

The previous entry on executing system commands described a technique of using inline Java code within ColdFusion, but this technique does not work for all executables.

The problem is that the standard output and error output streams need to be read simultaneously rather than sequentially. We can use a small Java application to help us in achieving this.

Installation

1. Download SystemCommand Component. This contains the Java source code and a systemcommand.jar file.

2. Add the systemcommand.jar file to your "Java class path".

This is done by either

a) Copying the systemcommand.jar file to your C:\ColdFusion8\lib directory (or the equivalent lib directory for your version of ColdFusion).

or

b) Adding the systemcommand.jar file to your Java class path within the ColdFusion administrator,

3. Restart the ColdFusion service so the new .jar file is picked up.

Usage

Once the systemcommand.jar file has been successfully installed, it can be used from within ColdFusion.

The system command object is created using the command:

<cfset syscmd = createObject("java","au.com.webcode.util.SystemCommand").init()>

This object has one function: execute(command,timeout).

<cfset result = syscmd.execute(command,timeout)>

The parameters are:

command The full command string to execute.
timeout (Optional) A timeout in milliseconds. The process will be terminated when the timeout is reached and an Exception will be thrown. The default timeout is 10 seconds.

Return Value

The value returned from the execute() function is an object with the following functions:

getCommand() The original command that was executed.
getStandardOutput() The standard output produced by the command.
getErrorOutput() The error output produced by the command.
getExitValue() An integer value provided by the process that was executed. A value of zero usually indicates that all was fine. A non zero value usually indicates a problem occurred.

Example

A simple example of ColdFusion code to use the system command utility.

<cfset command = "java -version">
<cfset syscmd = createObject("java","au.com.webcode.util.SystemCommand").init()>
<cfset result = syscmd.execute(command)>
<cfoutput>
   Command: #result.getCommand()#<br />
   ExitValue: #result.getExitValue()#<br />
   Error Output: #result.getErrorOutput()#<br />
   Standard Output: #result.getStandardOutput()#<br />
</cfoutput>

Download

Download SystemCommand Component.

Good luck!

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
26 Mar 2008 07:09AM
Danilo Celic said:
Danilo Celic's Gravatar Thanks for the tidbit about the Java version outputting to the error output stream. I recently ran into not being able to redirect the output for "java -version" to a file and now I know why that is the case, it wasn't "regular" output it was error output. I searched for quite a while and never ran into a reference that stated that it outputs to the error stream. I ended up writing a simple class file that output the Java version, and I then was able to redirect its output to the file I wanted it to, but I may switch back now that you mention this.

How did you find out that it outputs to the error stream? Did you just stumble upon it by trying to redirect to the error stream (which for some reason I never even thought to try it, or if I did I did it wrong some how), or did you know this from some other reference? If the later, do you have a link?
26 Mar 2008 11:54AM
Kevan Stannard's Gravatar Hi Danilo

Finding out that java -version goes to the error stream was unexpected. I was testing the previous version of this code (http://stannard.net.au/blog/index.cfm/2007/9/21/ex...) on various commands and discovered this by accident.
26 Mar 2008 02:34PM
Sami Hoda said:
Sami Hoda's Gravatar This is great! Will have to switch over from the CFC to the JAR now.
26 Mar 2008 02:38PM
Gabriel said:
Gabriel's Gravatar Just wanted to thank you for the post. It was one of those "hey, that's _exactly_ what I need" moments as I saw it in the RSS aggregator. So, thank you.
26 Mar 2008 03:35PM
Danilo Celic said:
Danilo Celic's Gravatar Kevan,

Thanks for the feedback on how you came across where Java outputs its version number.

I might just be able to use the JAR for other purposes, so thanks for the effort.
27 Mar 2008 04:27AM
Kevan Stannard's Gravatar Sami, nice to hear the CFC is still kicking along for you. Let me know if you have any issues with the new version.

Gabriel, Danilo, you're welcome!
6 May 2008 04:03PM
Russ Schmidt said:
Russ Schmidt's Gravatar You should post this to www.riaforge.org!

BTW I am an internal Adobe developer - NOT on the ColdFusion team - and this is strictly my own opinion.
7 May 2008 05:15AM
Kevan Stannard's Gravatar Russ, thanks for the suggestion - I will look into it.
31 May 2008 07:35PM
Mark Ireland said:
Mark Ireland's Gravatar Anyone know if this works on *nix, Solaris etc?
2 Jun 2008 04:50AM
Kevan Stannard's Gravatar Hi Mark

Not sure. I have posted your question on riaforge as well:

http://systemcommand.riaforge.org
19 Dec 2008 08:37AM
Chris said:
Chris's Gravatar Is it possible to specify a timeout 0 (non-blocking mode) and capture the PID of the spawned process? I'd like to run a short-lived server process and briefly interacts with Coldfusion, but I need the PID so I can terminate the process at the end of the request.
19 Dec 2008 01:38PM
Kevan Stannard's Gravatar Hi Chris

I don't believe we can access the PID from Java without extending out into some native win32 code, but for a non-blocking solution you may be able to do something like this:

<cfset cmd = "... your command ...">
<cfset runtime = createObject("java", "java.lang.Runtime")>
<cfset process = runtime.getRuntime().exec(cmd)>

You could stores the "process" variable in your session or application scope and have access to it at a later time to terminate. However getting access to the standard or error output is difficult to achieve with this approach.
19 Dec 2008 03:06PM
Chris said:
Chris's Gravatar Thanks Kevan! That's exactly what I need.
Add a comment
(will not be published)
(include http://)