JXA: robust replacement for doShellScript

Currently, doShellScript can’t be used in DT menu bar JXA scripts (it is fine in smart rule scripts, though). The handcrafted function below can be used as a replacement for menu bar scripts:

function runShellCommand(...arguments) {
  const err = $();
  const task = $.NSTask.alloc.init;
  task.executableURL = $.NSURL.fileURLWithPath('/bin/sh');
  const argumentString = arguments.join(" ");
  task.arguments=['--login', '-c', argumentString];
  const output = $.NSPipe.pipe;
  task.standardOutput = output;
  task.launchAndReturnError(err);
  const readHandle = output.fileHandleForReading;
  const rawResult = readHandle.readDataToEndOfFile;
  const result = $.NSString.alloc.initWithDataEncoding(rawResult, $.NSUTF8StringEncoding);
  return result.js;
}

The code is inspired by JXA with Require · GitHub
Call the function like so
const result = runShellCommand('cmd', 'arg1', 'arg2', 'arg3')
or
const result = runShellCommand('cmd arg1 arg2 arg3')
The arguments are passed as they are to the shell, so that runShellCommand('echo $PATH') returns the current PATH value. If you need to escape arguments, do so before calling runShellCommand.

runShellCommand is about 10% slower than doShellScript. That is due to the --login parameter passed to the shell. So, if you do not need the shell’s login environment, leave it out to speed things up.

1 Like