I know that I can create shortcuts on a vimrc file like the following:

:map <C-W> :w<CR>

However, let's say I have some different commands that I can execute on my shell:

notify-send "This is a bash script"
npm install
pm2 restart myservice

Is there any way of putting all this sequence of commands on my vimrc file while mapping a shortcut? Just so if I press <C-W> I'll be able to save my current file and execute shell commands from Vim?

  • 1
    I can’t really recommend mapping control-w, since all the window related keys live under that key
    – D. Ben Knoble
    Oct 15, 2021 at 13:05

1 Answer 1


Key things to know:

  • You can call shell commands from Vim using the function system().
  • Just like the shell itself you can combine shell commands in the system() parameter string by separating them with ;.
  • You can combine multiple Vim commands on a single command line using |.
  • When you use | in a mapping you must escape it as \|. <bar> also works.

Putting that all together we get...

noremap <C-W> :w \| call system('notify-send "This is a bash script"; npm install; pm2 restart myservice; randomCommandThatICreate')<CR>

Notice that I'm using :noremap rather than :map as you should always use that (and :nnoremap, :inoremap, etc.) unless you know for sure that you want remapping of the RHS.

Also, unless you really want to use this mapping in multiple modes it's advisable to use more specific map commands. For instance, this seems like the kind of thing you'd usually call from Normal mode so :nnoremap might be preferable.

Finally, you might want things to be a bit more manageable/edit-friendly than a single long mapping line. Here's one option...

let g:shellcmds = [
    \ 'notify-send "This is a bash script"',
    \ 'npm install',
    \ 'pm2 restart myservice',
    \ 'randomCommandThatICreate']

func RunShellCmds() abort
    for l:cmd in g:shellcmds
        call system(l:cmd)

noremap <C-W> :w \| call RunShellCmds()<CR>

Or without the function...

noremap <C-W> :w \| for shcmd in g:shellcmds \| call system(shcmd) \| endfor<CR>
  • Thanks! That's exactly what I needed... My only observation is that the notify-send command doesn't work to display messages. But that probably is a Linux related problem, just like crontab can't access commands that use the graphical interface without sourcing some paths and declaring the DISPLAY variable. I guess that Shell executed from Vim has these same limitations...
    – raylight
    Oct 15, 2021 at 3:12
  • 1
    Cool. I'm not too familiar with the inner workings of notify-send. This is about getting it to work in a cron script but maybe it's applicable here, too... Using notify-send with cron
    – B Layer
    Oct 15, 2021 at 3:28
  • Where is the output going? I've been trying to grab it with something like 'echo 333 1>>/tmp/greeting' within the shellcmds. but the file is just empty.
    – john-jones
    Oct 9, 2023 at 11:39
  • 1
    @john-jones It's normally captured and returned by system() but you can redirect it. However, your example doesn't look like valid syntax. If you try :call system("echo 333 > /tmp/greeting") that should work.
    – B Layer
    Oct 10, 2023 at 3:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.