Executing System Commands with ColdFusion

This entry has been updated!

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_02"
Java(TM) SE Runtime Environment (build 1.6.0_02-b06)
Java HotSpot(TM) Client VM (build 1.6.0_02-b06, mixed mode, sharing)

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 standard error 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 standard error 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

A better method would be to take advantage of Java’s underlying stream reading classes.

The basic idea is as follows:

<!--- Define our command --->
<cfset command = "java -version">
 
<!--- Get Java's runtime object which lets us execute system commands. --->
<cfset runtime = createObject("java", "java.lang.Runtime").getRuntime()>
 
<!--- Execute the command. --->
<cfset process = runtime.exec(command)>
 
<!--- Get the standard and error data streams. --->
<cfset standardInput = process.getInputStream()>
<cfset errorInput = process.getErrorStream()>
 
<!--- Let's start reading the standard input stream first. --->
 
<!--- Create a Java buffered stream reader, which gives us a readLine() function. --->
<cfset streamReader = createObject("java", "java.io.InputStreamReader").init(standardInput)>
<cfset buffererdReader = createObject("java", "java.io.BufferedReader").init(streamReader)>
 
<!--- Start reading the input stream. --->
<cfloop condition="true">
 
	<!--- Read one line of data. --->
	<cfset line = buffererdReader.readLine()>
 
	<!--- Note that the buffered reader returns a Java NULL when there is nothing
	left to read. When this gets back to ColdFusion this causes the variable
	it is assigned to to become undefined. So that is how we check if we have
	reached the end of the data. --->
	<cfif not isDefined("line")>
		<cfbreak>
	</cfif>
 
	<!--- Just output the line. --->
	<cfoutput>#line#<br></cfoutput>
 
</cfloop>
 
<!--- Then create another buffered reader to read the error stream ... --->

We can put this into a component which make the functionality a little easier to use:

Download SystemCommand Component

Example usage:

<cfset command = "java -version">
<cfset syscmd = createObject("component","SystemCommand").init()>
<cfset result = syscmd.execute(command)>
<cfdump var="#result#">

The value returned from the execute() function is a struct with the following fields:

command The original command that was executed.
standardOutput The standard output produced by the command.
errorOutput The error output produced by the command.
executeError A structure that contains error details if the execution failed.
This entry was posted in ColdFusion and tagged . Bookmark the permalink. Both comments and trackbacks are currently closed.

6 Comments

  1. David Stockton
    Posted September 21, 2007 at 8:48 am | Permalink

    Nice to know I’m not the only one stuck interfacing with certain apps that need to be called from the CLI.

    Good article.

  2. Posted October 27, 2007 at 12:53 am | Permalink

    Unfortunately there is a problem with this method – this may work for some applications but not all.

    The standard error stream and standard output streams should not be read sequentially – rather they should be read simultaneously. If they are not read simultaneously then the process may ‘block’ and appear to hang.

    So where to from here?

    1) Use the Batch File Wrapper method mentioned above, but this results in a few temp files that need to be cleaned up.

    2) Write a bit of Java code to do the work for you and call it from within CF.

    3) Possibly do this in CF8. The CFTHREAD tag might allow this to be done. Not sure.

  3. Sami Hoda
    Posted November 6, 2007 at 3:20 pm | Permalink

    Nice work. I’m using this in a project!

  4. Posted November 7, 2007 at 2:43 am | Permalink

    Hi Sami, great!

  5. Kelly
    Posted December 13, 2007 at 8:35 am | Permalink

    Very nice and useful method. It solves my problem

  6. Posted March 2, 2011 at 8:10 pm | Permalink

    Nice one. But as another person noted earlier, the java option also has its limits.

    If one was to execute something like ps -ef | grep 'java', it does not work. I’m sure there are many other examples.

    I’m surprised that CF makes such a mysterious (and a bit unusable) command. The only way I can reliably get it to work is to first write to a bat/shell script. And then execute that. This approach works well if you have limited requests of this nature. It does not scale.

    Ruby/Rails on the other had, provides a simple and powerful way to do this. Normally I don’t end up using either Ruby or ROR, but I think I might have to end up using it as issuing system commands is a critical part of the app that I’m working on at the moment.

One Trackback

  1. By Executing System Commands with ColdFusion on March 2, 2011 at 8:59 pm

    [...] This is an update to a previous entry. [...]