Skip to content

Instantly share code, notes, and snippets.

@isaacs
Last active August 29, 2015 14:20
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save isaacs/bb8003fe0153e558acac to your computer and use it in GitHub Desktop.
Save isaacs/bb8003fe0153e558acac to your computer and use it in GitHub Desktop.

New approach:

  • spawn('sh', ...)

    Find the -c arg.

    Parse, and split up by && and || and | and ;, expanding each bit.

    Then spawn the exploded command line. When we explode it, we need to do the same thing with looking up env and shebang results. However, if there are multiple parts, we can't change the actual thing that gets executed (ie, it must still be executed by calling sh, just with the exploded command line).

  • spawn('.../node', ...)

    Inject the args before the main, if it has a main file, and call spawn with that.

  • spawn('other', ...)

    Check if it's a shebang file. If so, explode it to the resulting cli: $interpreter $file. In most cases, this will result in env <envpairs> node <nodeoptions> $file

    Note that a shebang is strictly space-delimited arguments. Quotes and escapes are not allowed here, which makes it much simpler. We can simply resolve this to shebangLine.trim().split(/\s+/). If that first item is env then handle it just like a spawn(env).

  • spawn('env', ...)

    An env command line is done by looking up the resulting thingy with which(), and then resolving it further if it's a shebangK

  • Always treat bash, ksh, and zsh the same as sh, since they have similar cli string behavior.


Different ways to spawn things:

  1. spawn('node', ...) easiest. inject the args, add the envPairs

  2. spawn('sh', ['-c', 'X=y /path/to/node blah']) Parse '-c' arg just like a exec line. Windows flavor: spawn('cmd', ['/s', '/c', '"' + command + '"'])

replace ('sh', ['-c', '/path/to/node <inject args> blah']) I think if we do this right, we don't have to do exec() maybe?

  1. spawn('sh', ['-c', '/usr/bin/env x=y node blah blah']) This is the hard one, because the indirection capabilities are endless. sh can spawn bash which spawns env and so on.

A. replace with spawn('/usr/bin/env', ['x=y', 'node', '...']) WARNING: requires writing a sh-compatible command line parser. Splitting based on spaces won't work. Eg: sh -c 'command "long space"'

B. Write a node program that wraps cli commands, sort of like the wrap-main.js file. This will let us the just prepend it to the sh -c arg, and it can interpret the rest.

C. Maybe we just have our own node and iojs scripts (and node.cmd and iojs.cmd for win32 peops), and we put those in a bin folder that we always prepend to the PATH environ. Then it almost doesn't matter what shebangs or whatever get run, because which node will always return that thing. Something to consider. (Also, that fakey node script cannot be a node program, obviously, because the shell won't know where to find it!)
OOPS! no that won't work because how does the fake node shell script know what to inject to our wrap-main.js file? Computers are terrible.

  1. spawn('shebangscript', ['arg']) resolve shebangscript using which() read first line of script #!/some/interpreter some=args -> spawn('/some/interpreter', ['some=args', '$file']) then handle appropriately

  2. spawn('/usr/bin/env', ['x=y', 'node', '$file']) becomes case (1), spawn('node', ['$file'), but with the x=y appended to the envPairs list

  3. spawn('sh', ['-c', 'shebangscript arg'])

  4. exec('/path/to/node blah blah') becomes spawn('sh', ['-c', '/path/to/node blah blah'])

  5. exec('/usr/bin/env node blah blah') becomes spawn('sh', ['-c', '/usr/bin/env node blah blah'])

  6. exec('sh -c "shebangscript arg"') becomes spawn('sh', ['-c', 'sh -c "shebangscript arg"']) This is a dark pit of oblivion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment