7

I can catch the output of running a system command via system() or systemlist(), but that actually gives me the result of stdout and stderr combined. As I am only interested in stdout (and may use stderr for other purposes), this isn't useful for me.

How can I separate the output of a system command between stdout and stderr?

radlan
  • 677
  • 6
  • 15

3 Answers3

8

Vim's system() indeed captures both standard out and standard error by default. If you just want one of those, just use the normal shell means to redirect the other to the null device.

For example, ignore stderr (2>) with Bash:

:echo system('grep root /etc/passwd /doesnotexist 2>/dev/null')

On Windows, you'd use 2>NUL instead.

Alternatively, you could (temporarily) modify the 'shellredir' setting; e.g. by dropping the 2>&1 from it to ignore stderr. (Thanks @ChristianBrabandt for the tip!)

separate stdout and stderr

If you need both (but separately), that's a bit tricky. You could:

  • redirect one / both into a temp file and read that via readfile(), or via another system('cat /tmpfile')
  • invoke the command twice, ignoring first stdout and then stderr
  • add prefixes (via sed) to one / both (possible with fancy piping and redirections), read all in one fell swoop, and separate inside Vim
Ingo Karkat
  • 17,819
  • 1
  • 45
  • 61
  • 2
    Urgh, that's ugly. Being dependend on the operating system and additionally on the shell doesn't sound nice. The alternatives to separate stdout/stderr for using both also isn't funny, but I think your first approach for is doable without too much effort and still usable. What I dislike even more is the system dependency… As this seems to be the only way to achieve this, I accept your answer. Thank you very much! – radlan Nov 22 '19 at 14:17
  • No, it only caputes stderr, if the shellredir option contains 2>%1 and this depends on the shell option. So simply setting :set srr=> should fix capturing the error – Christian Brabandt Nov 22 '19 at 16:18
  • Well, external commands naturally depend on the environment. In practice, most Unix/Linux shells are very similar (unless you're using an exotic one), and if you use Cygwin or WSL on Windows, you could even get away with a single implementation. – Ingo Karkat Nov 22 '19 at 18:25
2

The separation can be done using jobs.

vim9script

job_start('grep root /etc/passwd /doesnotexist', { out_cb: On_stdout, err_cb: On_stderr, })

def On_stdout(ch: channel, msg: string)

do something with msg

enddef

def On_stderr(ch: channel, msg: string)

do something with msg

enddef

svlasov
  • 121
  • 3
  • Thank you for the example. But there is no need to use vim9script for it. Regular vimscript would do as well and would be way more compatible. – radlan Jul 13 '22 at 18:32
  • In my opinion, it is not a bad thing to start using vim9 script more often given its many improvements over legacy vimscript. – r_31415 Sep 04 '22 at 00:59
0
:h r! -- [range]r[ead] !{cmd}

looks like a possible alternative to system() to insert its standard output below the cursor or the specified line.

It says shellredir is used to save the output of the command, which can be set to include stderr or not as @IngoKarat mentioned.

:help set-default to put it back.

A similar question and answer: read from external command captures stderr as well.

Greek2me
  • 1
  • 1