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
<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:
<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 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. |

Good article.
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.