-
Notifications
You must be signed in to change notification settings - Fork 117
Shell and CLI Interactions
There are two methods by which you can execute shell scripts. The first, more "traditional" method, inherited from AppleScript, is the do shell script command. This method is severely limited. For example:
- Only one of
stderr
andstdout
is readily accessible (depending on if the last command returns 0), and even this is by no means guaranteed.app.doShellScript('false') // !! Error on line 1: Error: The command exited with a non-zero status. app.doShellScript('asdf; true') //
- Return values are not preserved (although they sometimes appear in an error message).
- Newlines are emitted as
\r
. Apple advises to pipe totr
.
app.doShellScript('ls')
// => "README.md\rbutton.svg"
A much more powerful method is to utilize the fact that the ObjC bridge can also be used to call C functions. JXA allows you to import many standard C libraries by header name, and others can be manually mapped.
For example, we can call system(3)
, and exit(3)
from stdlib.h
.
This way, exported variables in the environment correctly propagate through. Both standard out and standard error go where they should, and the script returns the correct return value.
The special function run
, with a parameter, creates an JavaScript array of the arguments passed to the script/applet.
So, when put together, the following script behaves exactly as if one were to use sh -c
:
#!/usr/bin/env osascript -l JavaScript
ObjC.import('stdlib')
function run(argv) {
argc = argv.length // If you want to iterate through each arg.
status = $.system(argv.join(" "))
$.exit(status >> 8)
}
You can also use NSProcessInfo
with the arguments
array:
var args = $.NSProcessInfo.processInfo.arguments
// Build the normal argv/argc
var argv = []
var argc = args.count // -[NSArray count]
for (var i = 0; i < argc; i++) {
argv.push( ObjC.unwrap( args.objectAtIndex(i) ) ) // -[NSArray objectAtIndex:]
}
delete args
To read a single variable, use the getenv()
function from C, For example, to get the application running (sometimes equivalent to argv[0]
):
ObjC.import('stdlib')
$.getenv('_') // This should always be '/usr/bin/osascript'
To get all of the environment variables, use NSProcessInfo
and unwrap the environment
dictionary to an object, then unwrap each string:
var env = $.NSProcessInfo.processInfo.environment // -[[NSProcessInfo processInfo] environment]
env = ObjC.unwrap(env)
for (var k in env) {
console.log('"' + k + '": ' + ObjC.unwrap(env[k]))
}
In shebang scripts, it is often useful to set exit code to signify success or failure.
ObjC.import('stdlib')
$.exit(123)
- Foreword
- Conventions Used in This Cookbook
- Using JavaScript for Automation
- ES6 Features in JXA
- Getting the Application Instance
- User Interactions
- User Interaction with Files and Folders
- Using Objective-C (ObjC) with JXA
- Shell and CLI Interactions
- Importing Scripts
- iTunes
- Keynote
- Messages
- System Events
- Safari & Chrome
- Script Editor
- XML
- Examples