Shell environment

I’ve done some experimenting with shell scripting in Drafts 19 and have run into a problem. This is long because I don’t want to leave out details that might be important.

First, scripts run through the ShellScript.create and execute system described in the docs run in an non-interactive shell, which means they have a very restricted PATH, just


So if you want to use a Perl, Python, or Ruby that’s more advanced than what comes with macOS (the stock Python, for example, is years out of date), you can’t simply start your script with a line like

#!/usr/bin/env ruby

as Greg does in the example in the docs, because env won’t see where you have the newer version of Ruby.

There are several ways to get around this, with varying degrees of flexibility. A method I’ve used is to give env a new PATH:

#!/usr/bin/env -S -P${HOME}/anaconda/bin:${HOME}/opt/anaconda3/bin:/usr/local/bin:${PATH} python

For me, this has two advantages:

  1. By using the HOME environment variable, it will start by looking in the user’s home directory without needing to know what the username is. This helps if you have two computers with different usernames and if you’re sharing a script with someone else.
  2. By giving a new, multidirectory PATH to search, you can account for differing installations across machines.

Unfortunately, when I’ve tried this in a single-step action using this script,

let script = `#!/usr/bin/env -S -P${HOME}/anaconda/bin:${HOME}/opt/anaconda3/bin:/usr/local/bin:${PATH} python
import sys
print('.'.join(str(x) for x in sys.version_info[:3]))`

let runner = ShellScript.create(script);

I get this error:

Script Error: ReferenceError: Can’t find variable: HOME
Line number: 1, Column 41

This suggests that the HOME environment variable isn’t set, but if I run this script

let script = `#!/bin/bash
echo $HOME`;

let runner = ShellScript.create(script);

it gives the right answer, which means HOME is set.

Anyway, the shebang line I’m using here for Python is one that works in Keyboard Maestro, which is the closest analog to Drafts I can think of. There are definitely ways I can get around this problem, but it does seem wrong that it isn’t working the way I expect it to. Maybe there’s a difference between running env directly and running it by way of JavaScript?

I expect there are aspects of this related to being Sandboxed (which Keyboard Maestro is not, I assume). Documentation for NSUserUnixScript is a bit scant. It was added specifically to support running a script outside the Sandboxed process in user space, but it does not really go into how it creates the environment used…and it does not support, as best as I’ve been able to ascertain, modifying the environment prior to execution.

I can get your result to work if setup the environment in bash and then run the python, like:

let script = `#!/bin/bash
export PATH=/b/foo:$PATH
python -c "import sys;print('.'.join(str(x) for x in sys.version_info[:3]))"
let runner = ShellScript.create(script);

if (runner.execute(["1", "2"])) {
	alert("STDOUT:\n" + runner.standardOutput);
else {
	alert("STDERR:\n" + runner.standardError);

I think the basic environment is the same or similar to what you would get using do shell script in AppleScript, which is also not loading in any bash_profile info, etc.

1 Like

Thanks! The sandboxing explanation makes sense. And I can handle different usernames by having that defined in a JavaScript variable at the beginning of the script and then interpolating it into the shebang line.