Using The Directory Attribute To Invoke CFExecute From A Working Directory In Lucee CFML 5.3.8.189
At InVision, I generate a lot of ZIP files using the CFExecute
tag and the zip
command-line utility. And, in most cases, I need to execute that zip
CLI from a "working directory" in order to maintain the input directory's local folder structure. Historically, I've had to execute the zip
command through a proxy command in order to use a working directory. However, with the release of Lucee CFML 5.3.8, the CFExecute
tag now has a directory
attribute! I'm super excited about this; so, I wanted to take a quick look at how it works.
The concept here is super simple. When you execute the CFExecute
tag, you can include a directory
attribute which will execute the given command name
from within that directory. The easiest way to demonstrate this is with the ls
command which will list a given directory's contents:
<cfscript>
execute
name = "ls"
directory = expandPath( "./" )
arguments = "-al"
variable = "results"
errorVariable = "errorResults"
timeout = 5
;
echo( "<pre>#encodeForHtml( results )#</pre>" );
</cfscript>
Normally, when I execute the ls
command, I provide it with a target directory as part of the CLI invocation arguments. However, if I omit the directory argument, the ls
command will just use the working directory; which is exactly what we're defining in the directory
attribute. So, when we run this ColdFusion code, we get the following output:
total 496
drwxr-xr-x 7 bennadel staff 224 Jul 1 06:32 .
drwxr-xr-x 122 bennadel staff 3904 Jul 1 06:31 ..
-rw-r--r--@ 1 bennadel staff 6148 Jul 1 06:05 .DS_Store
drwxr-xr-x 6 bennadel staff 192 Jul 1 06:04 data
-rw-r--r-- 1 bennadel staff 234987 Jul 1 06:32 data.zip
-rw-r--r-- 1 bennadel staff 225 Jul 1 06:53 test.cfm
-rw-r--r-- 1 bennadel staff 1616 Jul 1 06:05 test2.cfm
As you can see, we get the contents of the directory in which our ColdFusion code file, test.cfm
, is defined.
To make this a little more real-world, let's try executing the zip
command-line utility using the CFExecute
and the directory
attribute. When we do this, we're going to use ./
as the input directory (for zip
). This is going to get the zip
command to generate an archive using local folder paths which are relative to the working directory which, as you know, we're going to define using the directory
attribute.
<cfscript>
// Generate a ZIP archive of the given directory, maintaining local file / folder
// paths within the archive structure.
results = zipDirectory(
inputDirectory = expandPath( "./data" ),
outputFile = expandPath( "./data.zip" )
);
echo( "<pre>#encodeForHtml( results )#</pre>" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I generate a ZIP archive file for the given directory. The archive is generated
* using local file-paths.
*/
public string function zipDirectory(
required string inputDirectory,
required string outputFile,
numeric timeout = 5
) {
var zipArguments = [
// Recurse the input directory. This will include all of the nested
// directories within the input directory in the resultant archive.
"-r",
outputFile,
// Define the INPUT file - NOTE that this path is RELATIVE TO THE WORKING
// DIRECTORY (which will be defined by the "directory" attribute of the
// following CFExecute tag)! By using a relative directory, it allows us to
// generate a ZIP archive in which the relative paths of the input directory
// become the relative entries within the resultant archive.
"./"
];
execute
name = "zip",
directory = inputDirectory
arguments = zipArguments.toList( " " )
variable = "local.results"
errorVariable = "local.errorResults"
timeout = timeout
;
if ( len( errorResults ?: "" ) ) {
dump( errorResults );
abort;
}
return( results );
}
</cfscript>
As you can see, we're recursively (-r
) archiving the relative folders within the working directory. And, when we run this ColdFusion code, we get the following output:
adding: .DS_Store (deflated 96%)
adding: local folder 2/ (stored 0%)
adding: local folder 2/another-note.md (stored 0%)
adding: local folder 1/ (stored 0%)
adding: local folder 1/note2.txt (deflated 5%)
adding: local folder 1/note.txt (stored 0%)
adding: some images/ (stored 0%)
adding: some images/quokka.jpg (deflated 0%)
adding: some images/snowball-fight.jpg (deflated 0%)
As you can see, all of the file and folder paths in the archive are relative!
This is a minor change to the CFExecute
tag, but it's going to make generating my ZIP archive files so much easier. No more messing around with ProcessBuilder
, no more messing around with npm run
scripts, no more messing around with proxy commands; now, executing commands form a working directory is as simple as a directory
attribute!
Want to use code from this post? Check out the license.
Reader Comments