sindresorhus / execa Goto Github PK
View Code? Open in Web Editor NEWProcess execution for humans
License: MIT License
Process execution for humans
License: MIT License
I think it would be better if the following were basically equivalent:
const val = execa.sync(...args);
const val = await execa(...args);
This would mean a breaking change to execa.sync
, using child.spawnSync
instead, but I think it's worth it to have a more consistent API.
This code from the readme
was tested on windows 7:
execa.shell('echo unicorns').then(result => {
console.log(result.stdout);
});
and the result was no output at all. No error, unicorns
or anything.
I also tested known windows commands like dir
, and echo
:
execa.shell('dir').then(result => {
console.log(result.stdout);
});
execa.shell('echo unicorns').then(result => {
console.log(result.stdout);
});
None of these commands produced any results. Just nothing in the console at all.
However, when I run the execa
example, it works:
execa('echo', ['unicorns']).then(result => {
console.log(result.stdout);
});
//=> unicorns
Am I missing something simple?
See #27 (comment), and subsequent comments.
@kevva - If you could post a reproduction that proves this is a Node bug, we can file an issue in that repo and close this.
We need to think through the ideal API for this and #20 as well, but something like:
execa('./cli', [], {
transforms: {
// shortcut to a built-in transform we provide
stdout: 'line',
stderr: through2(function (chunk, encoding, callback) {
// custom processing here
})
}
}).then(function (result) {
result.stdout;
// => an array of lines
result.stderr;
// => an array of whatever the custom transform emits
});
child_process.execFileSync
already have this. Would be useful to have it for the async methods too.
You can already do it with child.stdin.write(stdin)
, but for tests especially, it would be convenient to specify it upfront.
Right now I'm doing this: https://github.com/sindresorhus/strip-indent-cli/blob/4998c5a41810e7316b1765b490f12f73fc4e4f39/test.js#L10
This would be so much nicer:
await execa('./cli.js', {input: ' foo\\n bar', cwd: __dirname});
I use this pattern a lot and have created a function for personal use:
const cp = execa('echo', ['foo', 'unicorns'])
cp.stdout.pipe(process.stdout)
cp.stderr.pipe(process.stderr)
Do you think it's a good idea to include it as a shorthand in the library? It could be named execa.echo
or execa.log
or execa.piped
...
Ran across a funky error case today while converting gulp-mocha to use execa. If {stdio: 'inherit'}
is used in execa's options, and the command fails, the resulting Error message contains nullnull
Line in question: https://github.com/sindresorhus/execa/blob/master/index.js#L201
Example output:
{ Error: Command failed: mocha /.../github/gulp-mocha/test/fixtures/fixture-fail.js
nullnull
at Promise.all.then.arr (/.../github/gulp-mocha/node_modules/execa/index.js:201:11)
at process._tickCallback (internal/process/next_tick.js:103:7)
FWIW, for commands that succeed, result.stdout
and result.stderr
are also both null in the .then(result =>
handler when {stdio: 'inherit'{
is used. That isn't mentioned anywhere in the README, and the option appears to be the only way to preserve terminal formatting.
execa
already cover the async methods and the sync methods could then take advantage of of the npm path thing, and utf8
by default instead of buffer
.
I'm using execa to start a child node process which simply starts an express server listening on port 5000.
But this child process often ends up running after I've exited the parent program, so when I run the parent again, I get an error saying port 5000 is already bound when it tries to start the child process. This happens about 25% of the time.
I haven't yet figured out what conditions cause the cleanup to fail. Any ideas?
My test print a warning
(node) warning: possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit.
I use execa 16 times in my unit tests
tests are here : https://github.com/lesspass/cli/blob/master/test.js
I try to test my command line interface with 16 tests like this one :
test('test space in password', async t => {
const {stdout} = await execa('./cli.js', ['lesspass.com', '[email protected]', 'my Master Password']);
t.is(stdout, 'onAV7&uvEC2=');
});
Here the output for my tests : https://travis-ci.org/lesspass/cli/jobs/169243714
Here the github repository to reproduce the bug : https://github.com/lesspass/cli
Node.js v4.6.0
linux 4.4.0-45-generic
ava version 0.16.0
excea version 0.5.0
npm version 3.10.8
How can I avoid to raise this warning ?
previously post on avajs/ava#1087
When you have a globally installed binary that uses execa
within it, the preferLocal
option doesn't work correctly.
Example: foo
is a js cli program that depends on a
(which has a binary installed to foo
's node_modules/.bin/ folder.
npm link # within foo directory
foo doSomethingThatCallsA # within another directory
output: spawn a ENOENT
The reason for this is because npmRunPath
uses the CWD by default. This isn't an issue in npm-run-path necessarily though, because execa should provide an option (along with preferLocal
to specify the directory to look into.
For example:
// foo.js
const execa = require('execa');
execa('a', ['some', 'options'], {
preferLocal: true,
localDir: __dirname
});
More than happy to PR this, just want to get approval on the localDir
option name or to have a better name suggested before I submit a PR ๐
We could just use execa('node', ['becomeTheOne.js'])
, but that would mean not capitalising on the added optimization that happens when using fork, as mentioned here.
[
<
i
m
g
4
'
'
g
m
a
r
t
i
g
n
y
'
2
4
2
4
g
m
a
r
t
i
g
n
y
]
(
h
t
t
p
s
:
/
/
i
s
s
u
e
h
u
n
t
.
i
o
/
u
/
g
m
a
r
t
i
g
n
y
)
h
a
s
b
e
e
n
r
e
w
a
r
d
e
d
.
IssueHunt has been backed by the following sponsors. Become a sponsor
#27 dropped execFile in favor of using spawn directly, so we've lost some features.
One is whatever these two lines do:
Can we reliably just do process.binding('uv')
? Or is that asking for problems. I know it means importing some native lib, but not much beyond that.
Maybe we look back all the way to Node 0.12
and make sure it was still imported and used the same back then?
This is more of a heads up to an interesting issue I've run into. I'm curious if others have seen it and if there's a known workaround.
foo.py (Python 2.7.10):
#!/usr/bin/env python
import sys
print(sys.stdout.encoding)
foo.js (execa v0.6.0, node 7.6.0):
'use strict'
const execa = require('execa')
const spawned = execa('./foo.py', [], { 'encoding': 'utf8' })
spawned.stdout.setEncoding('utf8')
spawned.then(({ stdout }) => console.log(stdout))
running node foo.js
:
None
I'm wondering how to ensure the invoked Python script has a stdout.encoding
of UTF-8
.
Undefined and null variables are passed to child process as their string representations, "undefined"
and "null"
. I'm not sure what the expected behavior is, but when working with shell scripts, I expect the undefined or null arguments to be passed as empty strings.
We lost this when we abandoned execFile.
It should probably be a feature of getStream
. I will open an issue there.
It might be possible to wait for the open
event on file streams (if we can find a way to detect that a stream is in fact a file stream, and that it will indeed eventually open).
See the discussion here
If the file given to execFile is not executable the error result from execFile is swallowed.
Could be useful to get what the terminal sees.
execa('ava').then(result => {
console.log(result.all);
});
I don't have the time to implement this right now, so help much appreciated :)
tomsotte earned $80.00 by resolving this issue!
- Checkout the Issuehunt explorer to discover more funded issues.
- Need some help from other developers? Add your repositories on Issuehunt to raise funds.
If I use execa.shell
to spawn a long running process, but the original process dies for some reason it would be great if the spawned process gets terminated automatically.
A small example
'use strict'
const execa = require('execa')
execa.shell('ipfs daemon')
.then((res) => {
console.log(res.stdout)
})
.catch((err) => {
console.log('failed', err)
})
console.log('starting')
setTimeout(() => {
console.log('exiting')
process.exit()
}, 10 * 1000)
Running this will print exiting
after 10s
and exit but when I grep for ipfs dameon
the process is still running.
I have created a simple script:
execa('bash',['./scripts/lint-styles.sh'])
.then(result => {
console.log(result.stdout);
});
This script calls
#!/usr/bin/env bash
./node_modules/.bin/sass-lint "./resources/assets/sass/*.s+(a|c)ss" -q -v
When executed, the output is correct, but it does not have the color output which displays when executing from cli
If you only pipe the output it's a waste of resources to buffer it and the user can also easily hit the maxBuffer
.
Either {buffer: false}
or detect when .then()
is used. I would prefer the latter, but not sure how feasible it is.
This would have prevented sindresorhus/gulp-mocha#169
Meaning can I live log the child process but then save the out resulting output to a variable?
Added in a80487b, but seems tests on Windows were failing then.
When I run my tests, which have this format:
import test from 'ava';
import execa from 'execa';
test('action', async (t) => {
const { stdout } = await execa('./cli.js', ['action']);
t.true(stdout.search('export default ClassName;') > -1);
});
I am getting the Error of Error: spawn EACCES
on each test run.
I have a feeling it could be something simple I am overlooking, like bad formatting or something, but figured I would ask here in case I have stumbled onto something. Is this error trying to tell me that the module doesn't have access to execa's index.js
file from within the node_modules
directory? I deleted the node_modules directory and reinstalled everything, but I got the same set of errors.
If you want to see more of the code in context, the module that is experiencing this issue is here.
Thanks in advance for your help.
It seems just as likely the info needed will be in stderr
, and it could be a whole bunch of data to print. Also, it gives you no means of extracting the original error.message
property.
Is this providing any real value? If people want to log it to the console, can't they just:
console.log(error.message + error.stdout);
This is not an issue, but more a question on node streams.
Just wanted to know if there is any technical difference between those 2 implementations
function runCommandAndWriteToFile (cmd, name) {
// cmd is an execa instance
return new Promise((resolve) => {
cmd.stderr.pipe(process.stdout)
cmd.stderr.on('end', () => {
cmd.then((res) => {
const msg = `Command run:\n${res.cmd}`
utils.log(chalk.magenta(msg))
resolve()
})
})
})
}
and
function runCommandAndWriteToFile (cmd, name) {
cmd.stderr.pipe(process.stdout)
return cmd.then((res) => {
const msg = `Command run:\n${res.cmd}`
utils.log(chalk.magenta(msg))
})
}
thanks
I'm using execa to start a process that runs "forever" (a web server). I would like to send a kill signal to the process about 30 seconds after launch, but I don't know how/where to get its process id from execa. Is it possible?
At the moment, execa returns a promise that is only resolved after the process ends, which isn't really useful for indefinitely-running processes. It would be nice if execa returned some additional info along with the promise, like the pid.
There are plenty of properties being attached to err
when we reject, that have potential value on a positive result as well.
Specifically, killed
and cmd
seem valuable.
I'm not sure what would be the best way to implement this, but it would be great if this module also captured the exit code of whatever file or command is ran somehow.
Feel free to close this as a wontfix
but I wanted to log the issue anyway.
With standard exec:
const exec = require('child_process').exec;
exec('networksetup', console.log.bind(console));
// 'networksetup Help Information...' Works OK
With execa:
const execa = require('execa');
execa.shell('networksetup').then(console.log.bind(console));
// Error: Command failed: /bin/sh -c networksetup
Another feature we lost with #27
See these lines from child_process
.
Right now we aren't destroying those streams. Probably should.
Could this one possibly be released? 0.4.1?
So that we don't get the annoying warning in all packages using execa ๐ผ
Thanks!
First of all thank you for this cool package! :)
I think one rather basic feature is still missing: Being able to cancel the child process easily. Using a cancelable promise (https://www.npmjs.com/package/cancellable or similar) seems like a good idea.
But maybe there is already another way?
Currently execa
rejects the promise on non-zero exit code. Sometimes though, you want to get the output regardless, like e.g. running ESLint and reading the result regardless of it having linter issues.
Not totally sold on the name. Suggestions welcome.
Does this option make sense?
Let's say that I have to run man ls
command.
// runs correctly, with man page of 'ls' in stdout
execa.shell('man ls')
.then(function (result) {
console.log(`stdout: ${result.stdout}`)
console.log(`stderr: ${result.stderr}`)
})
but
// this just runs 'man'
execa.shell('man ls', {shell: '/bin/bash'})
.then(function (result) {
console.log(`stdout: ${result.stdout}`)
console.log(`stderr: ${result.stderr}`)
})
Output
stdout:
stderr: What manual page do you want?
Hi. Just got this message:
npm WARN deprecated [email protected]: cross-spawn no longer requires a build toolchain, use it instead!
Is it relevant here?
Is there any reason that execa.spawn
isn't promisified as execa
?
Steps to reproduce:
touch foo.sh
chmod -x foo.sh
to be explicit.npm i execa
execa('foo.sh')
as below// index.js
const execa = require('execa')
execa(__dirname + '/foo.sh')
.then(() => console.log('Ran successfully'))
.catch(err => console.error('Caught an error', err))
Crashes with error message
/repos/node_modules/execa/index.js:120
spawned.then = promise.then.bind(promise);
^
TypeError: Cannot set property 'then' of undefined
at module.exports (/repos/node_modules/execa/index.js:120:15)
at Object.<anonymous> (/repos/index.js:2:1)
at Module._compile (module.js:556:32)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)
at Function.Module._load (module.js:424:3)
at Module.runMain (module.js:590:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
Are there any annoyances you've had with child processes that could potentially be fixed in execa? Anything that could be done better? I'm open for any kind of suggestions.
I think the stdio
array is a pretty poor UI, you have to remember the order of things. I'm constantly pulling up that piece of documentation. Instead, of the three member array, let's just give each one it's own named property:
execa('./cli.js', [], {
stdin: someReadableStream,
stdout: 'pipe',
stderr: 'inherit'
});
For stderr
and stdout
the choice between inherit
and pipe
, has tradeoffs.
Specifically, if you choose inherit
, you loose the ability to observe the stream, it's piped straight to the parents stdout/stderr.
I propose we add a tap
option. It will be converted internally to the pipe
option, but we will grab the pipe, split it into two streams, one stream we pipe directly to stdout/stderr
, the other we make available for observation.
#27 Removed the timeout option. We should add it back before the next release.
Relevant sections from execFile
:
promoted from #6 (comment)
Sometimes you want to use execa.spawn()
because you need an object representing the child process while it's running, so you can send signals or listen to events. But then you lose the convenience of a promise-based API (i.e. having a thing you can easily await
, which will collect up the stdout/stderr for you).
Suggest adding a method to spawned child objects. This method would return a promise that resolves when the child process exits.
const child = execa.spawn(...);
// do any necessary manual stuff with events/signals etc
child.on(...);
// await the final outcome
const { stdio, stderr, code } = await child.exited();
(I'm not sure about the name exited
though.)
Possible extra feature: the method could take an optional parameter specifying the expected exit code, e.g. .exited(0)
. If the child process exits with any other code, the promise rejects. If no exit code is specified, the promise will always resolve when the child process exits, no matter how.
UPDATE: I think this is the right way to do it. Everything in between here and there is interesting background though.
I think you should be able to do this:
const PassThrough = require('stream').PassThrough;
const execa = require('execa');
const stdin = new PassThrough();
const stdout = new PassThrough();
const stderr = new PassThrough();
execa('foo', [], {stdio: [stdin, stdout, stderr]});
But you aren't actually allowed to pass any old stream to child_process
. It has to be a very specific type of native stream (here is the internal Node code that checks that).
Instead, if you've got some userland stream you want to use, you've got to go about it a bit more convoluted way:
const PassThrough = require('stream').PassThrough;
const execa = require('execa');
const stdin = new PassThrough();
const stdout = new PassThrough();
const stderr = new PassThrough();
const cp = execa('foo', [], ['pipe', 'pipe', 'pipe']);
stdin.pipe(cp.stdin);
cp.stdout.pipe(stdout);
cp.stderr.pipe(stderr);
I think it's a weird choice to allow streams, but only certain types of streams. I've written native-stream-type
which would help us figure out if we can pass the stream directly to child_process.spawn
or not. For userland streams, we just convert to using pipe
.
var child = execa('./cli.js', [], {stdio: [null, 'observable', null]});
t.plan(5); // should get 5 lines
await child.stdin
.filter(line => /^s/.test(line)) // only lines that start with "s"
.forEach(line => t.true(fitsSpecifiedFormat(line)));
Additionally, it would be cool if you could use DuplexStream
s to modify what reaches the observable, I'm thinking "newline-separated-json" byte stream converted to a JSON object stream, etc.
There are a number of "split-stream-XXX" modules already on NPM, but they convert to a stream, would be cool to write object-stream-to-observable
, and then use that to make CLI output processing awesome.
Currently it returns the error as an .error
property, but I think that's a bit surprising (the async methods reject). At least I didn't realize: sindresorhus/clipboardy@82acb3b
Thoughts anyone?
Could you release a new version with the reject
option available?
#27 really only went so far as copying the execFile implementation where necessary to get tests passing using spawn.
Most of the required test aren't terribly consequential, but should still be completed. The one set of tests I can think of right now is the creation of the Error property. It builds the command by concatting args together, then uses that to build the error message. Also, it attaches a few extra properties: killed
, code
, etc.
Examine the diff of #27 for a complete picture of what should be tested.
I will tackle this, just recording for posterity.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.