CFShell: A command line interface for CFML engines
Many times when we are working on our ColdFusion apps there are situations in which we want to quickly evaluate something or try some one or two-liner snippets to do something quick. The typical process then is that we have to create a .cfm page, put in on the server, go to the browser and execute it. And that's pretty much the only way we have for interacting with the CFML engine. This contrasts with other languages like Ruby, Python, or even PHP in which you can quickly interact with the language directly from a command line or terminal window. Wouldn't it be nicer to have the inmmediate satisfaction of evaluating our CFML/cfscript statements interactively? Well, that's where CFShell comes in.
CFShell is an attempt to provide that level of interaction with the CFML engine. ColdFusion is pretty intertwined with the web/http environment, so a pure command line interface is a bit tricky (at least without serious Java hacking), so the next best thing is to provide a front end for interacting with the CF server through HTTP requests, but running it from the command line. CFShell is composed of two elements: a client and a server.
The CFShell client is written in Python, and acts as a front end for sending/receiving HTTP requests to the CF server. The server part is just a simple script (cfshell.cfm) that receives and executes the statements sent by the client. The client can send anything that can be put on a CFM template, for example:
>> <cfset a = 1>
>> #createObject("component","path.to.component").getSomeValue()#
All statements executed have an implicit cfoutput around.
For security the server part would only accept commands sent from the localhost, however this is NOT A TOY FOR PRODUCTION SERVERS!!!
Also, there wouldn't be any point of being interactive if we couldn't maintain some state between our commands; for that reason, cfshell uses the session scope to sync its local state between calls. Each time you start the cfshell client, it starts a new session. This allows us to do thins like:
>> Hello, my name is #myname#
Hello, my name is oscar
How To Use CFShell
To use CFShell you need of course to have Python installed on your computer. The client was built using python 2.6. Also you need to be running the CF server locally. I developed this on Railo 3, but I don't see any reason why it shouldn't work with any of the other engines.
By default, the cfshell client will try to connect to http://localhost/cfshell/cfshell.cfm but you can override this by passing an argument to the cfshell.py script. The script will always connect to a template named cfshell.cfm, however you can place this template on any application that you wish to interact with.
For example if you want to interact with an application in http://localhost:8080/someapp/ you need to copy cfshell.cfm to somapp/ and then do:
Also, you can use any alias, subdomain or port as long as it resolves to the localhost (127.0.0.1). This restriction is enforced on cfshell.cfm, and you can take it out at YOUR OWN RISK if you want to use cfshell on a different server.
CFShell Commands
Once the prompt appears, you can type .help for a list of commands. Here are the available commands that you can use:
.help : displays the list of available commands
.cfscript < staments > : executes the rest of the line as cfscript code
.get < template_path > : does a GET request to the given page (output supressed)
.sget < template_path > : same as .get without supressing output
.post < template_path > < arguments > : does a POST request to the given template (output supressed)
.spost < template_path > < arguments > : same as .post without supressing output
.call < template_path > : does a cfcinclude of the given template
.exit : exits cfshell
Any other string that you sent will be evaluated as a CFML statement, and any output generated will be displayed on the next line.
POST and GETs
Additionally cfshell client lets you do POST and GET requests to any page within the directory that you connected. Use the .get and .post commands for this.
For a GET request you only need to pass the page that you want to call, for example:
This will send a GET http request to http://localhost/cfshell/index.cfm?a=1&b=2 (assuming that you connected to localhost/cfshell)
When doing a POST request you will most likely want to pass data on the body of the request. The format for doing this is like this:
Both .post and .get will only show you the status code of the request. If you want to actually see the HTML that is returned, use .spost and .sget
CFShell Example
Here is the output of a cfshell session:
CFShellClient :: Version 0.1
URL: http://localhost/cfshell/
CFID/CFTOKEN: 33b82da2%2D7cc4%2D4ac0%2D842c%2Dcd6a601d9fd8/0
Type .help for available commands
>> Today is #lsDateFormat(now())#
Today is Oct 7, 2009
>> <cfset myname = "oscar">
>> My name is #myname#
My name is oscar
>> .cfscript for(i=1;i<11;i++) {writeoutput("#i#,");}
1,2,3,4,5,6,7,8,9,10,
>> .get index.cfm
GET http://localhost/cfshell/index.cfm
200
>> .sget index.cfm
GET http://localhost/cfshell/index.cfm
<h1>CFShell</h1>
Use cfshellclient to connect.<br /><br />
>> do you know the value of #somevar#?
[500] variable [SOMEVAR] doesn't exist
>> .exit
Bye!
You can download CFShell from RIAForge here
Enjoy!
Could you remove the need for the "pthon" before it by adding a shabang at the top of the script? That would probably also remove the need for the .py at the end too. So, instead of "$ python cfshell.py [params]", you could simply enter "$ cfshell [params]" and duplicate what php has going for it.
Further, I wonder if you could make Python talk directly to the j2ee connector port and remove the need to install CFML files in a "website" in order to get your CFML processed. IE: tell Python to use the same port that Apache uses to pass requests off to the J2EE server to get them process, then return the result via command line, just like apache returns the processed HTML to a browser.
Sweet creation there Oscar!
Yeah, I spent a few hours yesterday doing this and realized that I opened a pandora's box of possibilities. I have a dev version on my linux box that is an executable, so it doesn't need the python call, and also has history on the command line; but I wanted at least the first iteration to be cross platform. I'm a complete newbie in python programming, so I'm learning as I go.
I like the idea of using the same mechanism that Apache to talk to the J2EE server; will explore more on that.
Glad you like it!