Tinderbox v9 Icon

runCommand(commandStr[, inputsStr, dirStr])


Operator Type: 

Operator Scope of Action: 

Operator Purpose: 

Operator First Added: 

Operator Last Altered: 

Operator Uses Regular Expressions: 

Operator Has Optional Arguments: 


runCommand(commandStr, inputStr)

runCommand(commandStr, inputStr, dirStr) /runCommand(commandStr, , dirStr)

The operator, runCommand(), lets rules and actions use the command line with an assumed working directory of the users OS home folder: see below for using dirStr for setting a different context of execution. The full detail of command line is out of scope for aTbRef and should be researched online. For novices, a good approach is to make a command line work in the macOS Terminal and only then try to use it via runCommand(). Similarly, leave customising the input via attribute/variable values until after a fixed string is working as this makes it much easier to troubleshoot the correct issue.

Understanding the latter can help avoid trying to echo data into a command within the 'commandStr' string. See the 'quotify' example further below.

runCommand(commandStr)

The operator passes commandStr to the OS's Unix shell. The new shell processes commandStr. The process's standard output (stdout) is returned as the result of calling runCommand(). However, there is no requirement for a left-side argument if the output of runCommand() is needed, e.g. if it is just a true/false success/fail message.

For example, to set the $Text of a note to a listing of all files/folders in the users home directory ~, run this code as a stamp:

$Text = runCommand("ls -a"); 

Whereas a stamp:

runCommand("ls -a"); 

would run the same code but the listing would not be used. Not useful in this example but if the need it just to run a script where the reult is not of interest, runCommand() can called on its own.

runCommand(commandStr, inputStr)

The operator passes commandStr to the OS's Unix shell. The new shell process receives inputsStr, if any, as its standard input stdin. The process's standard output (stdout) is returned as the result of calling runCommand(). The above syntax, in one-line command line usage, can also be thought of as: runCommand(command, stdIn).

For example, the wc (word count) command can take a file or literal string as an input. On the command line, an echo command would be needed to pipe the literal string to wc, like so:

echo "hello world" | wc -w 

to get the result '2' (two words in the input string) in Terminal. To do this via runCommand():

runCommand("wc -w","hello world"); 

But, you don't see the returned value unless you pass the output of the command to something. a number is expected here so $MyNumber is sensible start:

$MyNumber = runCommand("wc -w","hello world"); 

and $MyNumber will now have the value '2'.

Or, if using attributes or variables to pass in the data:

	var:string vCmd = "wc -w";
	var:string vArgs= $MyString; // which has the text whose word count we want
	$MyNumber = runCommand(vCmd, vArgs);

Whether the desired command line tool accepts inputs on stdIn is something the user needs to understand before using this command.

IMPORTANT: Note that if using inputsStr, to pass arguments otherwise normally passed as part of commandStr, it is necessary to prefix the command used in commandStr with 'xargs' so that the arguments passed via stdin are correctly handled. Thus if using the command "mv" in commandStr with arguments in inputStr, the commandStr needs to be "xargs mv". This is shown in examples further below under heading "Working around using literal strings as inputs".

Setting the working directory using dirStr

runCommand(commandStr, inputStr, dirStr)

Where is the working directory location assumed for executing scripts?

If the optional dirStr is specified as a POSIX path, it sets the working directory in which commandStr is executed. Otherwise, by default, the working directory is the user's home folder , i.e. ~ or /Users/[shortusername] or in full form.

If using dirStr, an inputStr must be provided even if only an empty string: runCommand(commandStr, "", dirStr).

dirStr can be passed either as a full path from drive root or using the tilde (~) notation. dirStr should not include a forward slash at the end: e.g. "~/test" not "~/test/". The last part of the supplied path should be the folder which is to be the context of execution. Thus "~/test" implies a folder test that is a child folder of the users home folder.

Working around using literal strings as inputs

Any or all of commandStr, inputsStr, or dirStr may use attribute or variable values in place of literal strings. Thus:

runCommand("xargs mv", '"x/tb 1.png" "y/tb 1.png"', "~/test") 

will move file 'tb 1.png' from sub-folder 'x' at path '~/test' to sub-folder 'y'. (Note: the source and target paths need quoting because the file 'tb 1.png' has a space in its name and the paths would otherwise be mis-parsed by the shell. If the various strings.). Placing the start and end relative paths into $SourcePath and $TargetPath, the command above could also be written clearly/verbosely as:

	var:string vCmd = "mv";
	var:string vCmd += '"'+$SourcePath+'" "'+$TargetPath+'"';
	vFolder = "~/test";
	runCommand(vCmd, "", vFolder);

or, the same but passing some of the arguments as inputs using the xargs method:

	var:string vCmd = "xargs mv";
	var:string vArgs = '"'+$SourcePath+'" "'+$TargetPath+'"';
	vFolder = "~/test";
	runCommand(vCmd, vArgs, vFolder);

Further examples

If a note called "Jane Doe" is dropped on a container with this OnAdd action:

$MyResult = runCommand("sendmail -f "+$Email+" "+$Email(parent), "Subject:"+$Name+"\nHello\n.") 

This assumes both dropped and container notes have a valid email address in Email. If so, Jane Doe will get an email with subject line "Jane Doe" and body text "Hello"; the email will be from the dropped note's $Email address and to the container's $Email address. In the above example:

commandStr: sendmail - f jade@doe.com someone@other.com' 

inputsStr (i.e. via stdin): "Subject: Jane Doe\nHello\n." 

User attribute 'MyResult' will receive any message back from standard output.

The runCommand() operator does not require a left-side to the expression where the result of the command line is not needed by Tinderbox. Thus the same example as above can run as:

runCommand("sendmail -f "+$Email+" "+$Email(parent), "Subject:"+$Name+"\nHello\n.") 

Bear in mind that in this latter case there is no way of knowing if the command executed successfully.

To use external commands like above you may need to check the encoding of the strings you extract from your TBX attributes. Do not forget to allow for characters like spaces/quotes/apostrophes in attribute values; these will invariably need escaping for safe use in a command line using operators like urlEncode():

$MyResult=runCommand("/usr/bin/curl -d 'status="+urlEncode($Name)+"' -u myusername:mypassword https://twitter.com/statuses/update.atom"); 

In the above, if the value of $Name were "Mark's project", the use of urlEncode() will ensure the string passed to the command line is actually "Mark%27s%20project".

exportedString() can also help with ensuring the necessary encoding, allowing a template to be used to help with formatting the string passed into the command.

Exactly where you do/do not need to encode attribute values will depend on the syntax of the particular operation you are performing.

To help get around issues of quoting, a note's text (or other string attribute) can be used for either or both arguments. Consider a note called 'quotify' holding this command line:

sed 's:"\([^"]*\)":“\1”:g' 

A stamp might then hold this code:

$Text = runCommand($Text("quotify"),$Text); 

This effectively adds a menu item to the Stamps to change ordinary double quotes to their 'smart form', allowing the process to be run once, on demand, avoiding repeat use every agent cycle as would happen if using a rule or agent action.

Dealing with inline quote characters

Because input arguments are parsed for regular expressions (by the OS, not Tinderbox), it may be possible to use the '\dnn' form described here to work around the lack of escaping from single double quotes within strings.

Slow running target processes

This operator was designed for rapid call+response, not as a call-and-wait process so should not be used for process that are slow to return data. There is no fixed timeout but it is probably c.30 seconds. Slow to complete processes should be set to write to disk and the data subsequently read from disk, using two separate actions.

Debugging runCommand() calls

See the dubugging section article on Use of the Command Line.

Legacy only

Using just command on its own is akin to using the back-tick action syntax.