Skip to main content
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Hanna Eckert
Ben Nadel at CFCamp 2023 (Freising, Germany) with: Hanna Eckert

Using The Directory Attribute To Invoke CFExecute From A Working Directory In Lucee CFML 5.3.8.189

By
Published in

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

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel