Merge #5502 from justinmk/error-write-to-bg-process

system('foo &', 'bar'): Show error, don't crash.
This commit is contained in:
Justin M. Keyes 2016-10-19 02:09:29 +02:00 committed by GitHub
commit 6a6f188d2a
7 changed files with 44 additions and 23 deletions

View File

@ -6838,23 +6838,30 @@ system({cmd} [, {input}]) *system()* *E677*
Get the output of the shell command {cmd} as a |string|. {cmd} Get the output of the shell command {cmd} as a |string|. {cmd}
will be run the same as in |jobstart()|. See |systemlist()| will be run the same as in |jobstart()|. See |systemlist()|
to get the output as a |List|. to get the output as a |List|.
Not to be used for interactive commands.
When {input} is given and is a string this string is written If {input} is a string it is written to a pipe and passed as
to a file and passed as stdin to the command. The string is stdin to the command. The string is written as-is, line
written as-is, you need to take care of using the correct line separators are not changed.
separators yourself. If {input} is a |List| it is written to the pipe as
If {input} is given and is a |List| it is written to the file |writefile()| does with {binary} set to "b" (i.e. with
in a way |writefile()| does with {binary} set to "b" (i.e. a newline between each list item, and newlines inside list
with a newline between each list item with newlines inside items converted to NULs).
list items converted to NULs). *E5677*
Pipes are not used. Note: system() cannot write to or read from backgrounded ("&")
shell commands, e.g.: >
:echo system("cat - &", "foo"))
< which is equivalent to: >
$ echo foo | bash -c 'cat - &'
< The pipes are disconnected (unless overridden by shell
redirection syntax) before input can reach it. Use
|jobstart()| instead.
Note: Use |shellescape()| or |::S| with |expand()| or Note: Use |shellescape()| or |::S| with |expand()| or
|fnamemodify()| to escape special characters in a command |fnamemodify()| to escape special characters in a command
argument. Newlines in {cmd} may cause the command to fail. argument. Newlines in {cmd} may cause the command to fail.
The characters in 'shellquote' and 'shellxquote' may also The characters in 'shellquote' and 'shellxquote' may also
cause trouble. cause trouble.
This is not to be used for interactive commands.
The result is a String. Example: > The result is a String. Example: >
:let files = system("ls " . shellescape(expand('%:h'))) :let files = system("ls " . shellescape(expand('%:h')))
@ -6869,9 +6876,6 @@ system({cmd} [, {input}]) *system()* *E677*
The command executed is constructed using several options when The command executed is constructed using several options when
{cmd} is a string: 'shell' 'shellcmdflag' {cmd} {cmd} is a string: 'shell' 'shellcmdflag' {cmd}
The command will be executed in "cooked" mode, so that a
CTRL-C will interrupt the command (on Unix at least).
The resulting error code can be found in |v:shell_error|. The resulting error code can be found in |v:shell_error|.
This function will fail in |restricted-mode|. This function will fail in |restricted-mode|.

View File

@ -108,6 +108,7 @@ Options:
Commands: Commands:
|:CheckHealth| |:CheckHealth|
|:drop| is available on all platforms
|:Man| is available by default, with many improvements such as completion |:Man| is available by default, with many improvements such as completion
Functions: Functions:
@ -140,10 +141,10 @@ are always available and may be used simultaneously in separate plugins. The
`neovim` pip package must be installed to use Python plugins in Nvim (see `neovim` pip package must be installed to use Python plugins in Nvim (see
|provider-python|). |provider-python|).
|:!| and |system()| do not support "interactive" commands; use |:terminal| for |:!| does not support "interactive" commands. Use |:terminal| instead.
that instead. Terminal Vim supports interactive |:!| and |system()|, but gui (GUI Vim has a similar limitation, see ":help gui-pty" in Vim.)
Vim does not. See ":help gui-pty" in Vim:
http://vimdoc.sourceforge.net/htmldoc/gui_x11.html#gui-pty |system()| does not support writing/reading "backgrounded" commands. |E5677|
|mkdir()| behaviour changed: |mkdir()| behaviour changed:
1. Assuming /tmp/foo does not exist and /tmp can be written to 1. Assuming /tmp/foo does not exist and /tmp can be written to

View File

@ -112,8 +112,8 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf)
// to `alloc_cb` will return the same unused pointer(`rbuffer_produced` // to `alloc_cb` will return the same unused pointer(`rbuffer_produced`
// won't be called) // won't be called)
&& cnt != 0) { && cnt != 0) {
DLOG("Closing Stream(%p) because of %s(%zd)", stream, DLOG("Closing Stream (%p): %s (%s)", stream,
uv_strerror((int)cnt), cnt); uv_err_name((int)cnt), os_strerror((int)cnt));
// Read error or EOF, either way stop the stream and invoke the callback // Read error or EOF, either way stop the stream and invoke the callback
// with eof == true // with eof == true
uv_read_stop(uvstream); uv_read_stop(uvstream);

View File

@ -545,6 +545,16 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer,
static void shell_write_cb(Stream *stream, void *data, int status) static void shell_write_cb(Stream *stream, void *data, int status)
{ {
if (status) {
// Can happen if system() tries to send input to a shell command that was
// backgrounded (:call system("cat - &", "foo")). #3529 #5241
EMSG2(_("E5677: Error writing input to shell-command: %s"),
uv_err_name(status));
}
if (stream->closed) { // Process may have exited before this write.
ELOG("stream was already closed");
return;
}
stream_close(stream, NULL, NULL); stream_close(stream, NULL, NULL);
} }

View File

@ -1,7 +1,3 @@
-- Specs for
-- - `system()`
-- - `systemlist()`
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local eq, clear, eval, feed, nvim = local eq, clear, eval, feed, nvim =
helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.nvim helpers.eq, helpers.clear, helpers.eval, helpers.feed, helpers.nvim
@ -120,12 +116,22 @@ describe('system()', function()
it('returns the program output', function() it('returns the program output', function()
eq("echoed", eval('system("echo -n echoed")')) eq("echoed", eval('system("echo -n echoed")'))
end) end)
it('to backgrounded command does not crash', function()
-- This is indeterminate, just exercise the codepath.
eval('system("echo -n echoed &")')
eq(2, eval("1+1")) -- Still alive?
end)
end) end)
describe('passing input', function() describe('passing input', function()
it('returns the program output', function() it('returns the program output', function()
eq("input", eval('system("cat -", "input")')) eq("input", eval('system("cat -", "input")'))
end) end)
it('to backgrounded command does not crash', function()
-- This is indeterminate, just exercise the codepath.
eval('system("cat - &", "input")')
eq(2, eval("1+1")) -- Still alive?
end)
end) end)
describe('passing a lot of input', function() describe('passing a lot of input', function()