mirror of
https://github.com/neovim/neovim.git
synced 2025-12-11 17:12:40 +00:00
system('foo &', 'bar'): Show error, don't crash.
Closes #3529 Closes #5241 In Vim, :echo system('cat - &', 'foo') works because for both system() and :! Vim writes input to a temp file and uses shell syntax to redirect the file to the backgrounded `cat` (get_cmd_output() .. make_filter_cmd()). In Nvim, :echo system('cat - &', 'foo') fails because we write the input directly via pipes (shell.c:do_os_system()), but (per POSIX[1]) backgrounded process input stream is redirected from /dev/null (unless overridden by shell redirection; supported only by some shells [2]), so our writes are ignored, the process exits quickly, and if we are writing data larger than the buffer size we'll see EPIPE. This still works: :%w !tee > foo1358.txt & but this does not: :%w !tee foo1358.txt & though it *should* (why doesn't it?) because we still do the temp file dance in do_bang() .. do_filter(). [1] http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_03_02 [2] http://unix.stackexchange.com/a/71218
This commit is contained in:
@@ -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|.
|
||||||
|
|
||||||
|
|||||||
@@ -141,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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user