Vim Freezes on :! and External Commands: Diagnostic Guide
Vim hangs after :! or :shell because of shell, shellcmdflag, or TERM misconfiguration. Here is how to isolate and fix it.
28 Apr 2026, 15:21 UTC

What the problem looks like
You run :!git status, :shell, or a filter such as :%!sort, and Vim stops responding. The screen may go blank, the cursor may disappear, or the editor may appear frozen until you kill the terminal or force-quit Vim. This is not normal behavior: Vim should launch the external process, wait for it to finish, and return control.
Useful takeaway: most freezes are caused by Vim's shell options or terminal environment, not by Vim itself. An incompatible login shell, a missing -c flag in shellcmdflag, or a dumb or unset TERM variable can make Vim start a shell that waits for input or emits control sequences Vim cannot interpret.
Recognizable condition and likely causes
| Symptom | Likely cause | First check |
|---|---|---|
Vim hangs after :!cmd and never returns | shell set to an interactive shell such as fish without matching shellcmdflag | :set shell? shellcmdflag? |
| Blank terminal window opens and waits | shellcmdflag missing -c or using interactive flags | Run vim --clean and test :!echo ok |
| Garbled output or frozen screen after a filter | TERM is dumb, unset, or mismatched; shellredir or shelltemp misconfigured | :echo $TERM inside Vim |
| Command blocks waiting for input | External command reads stdin, for example less or a nested editor | Redirect stdin or use :terminal |
Ordered diagnostic checks
- Isolate Vim from your configuration. Run
vim --cleanin a terminal, then execute:!echo ok. If the command returns immediately, the freeze comes from yourvimrcor a plugin, not from Vim's core shell handling. - Inspect the shell options. Inside Vim, run
:set shell? shellcmdflag? shellredir? shelltemp?. Compare the values against the defaults for your platform in:h shelland:h shellcmdflag. On most Unix-like systems,shellshould point toshorbash, andshellcmdflagshould contain-c. - Check the terminal environment. Run
:echo $TERMand:echo $SHELLinside Vim.TERMmust not bedumbor empty, and$SHELLshould match the configuredshelloption unless you intentionally override it. - Find who changed the option. Run
:verbose set shell?. The output names the last script that setshell, which is often a plugin manager or a shell-specific vimrc block. - Reproduce the command outside Vim. In your terminal, run the exact construction Vim would use. For example, if
shellis/bin/bashandshellcmdflagis-c, testbash -c 'echo ok'. If that hangs or waits for input, the problem is the shell invocation, not Vim.
Fixes tied to each finding
If vim --clean works but your normal session does not
Bisect your vimrc or disable plugins one at a time. Look for lines that set shell, shellcmdflag, or mappings that wrap :!. A plugin manager may set shell to fish for its own install steps and forget to restore it.
If shell is fish or another interactive shell
Vim expects shell to execute a single command and exit. Fish is designed as an interactive login shell and often does not behave like sh under -c. Switch shell back to bash or sh for Vim's external commands:
set shell=/bin/bash
set shellcmdflag=-c\
Note the backslash before the space if your shellcmdflag contains arguments with spaces; Vim parses this option carefully. If you must use fish, you need to coordinate shellcmdflag, shellpipe, and shellredir together; changing only shell usually creates quoting bugs.
If shellcmdflag is missing -c
Vim builds commands as {shell} {shellcmdflag} {command}. Without -c, many shells start an interactive session and wait for user input instead of running the command. Confirm the value ends with -c or the equivalent for your shell.
If TERM is dumb or unset
Set TERM in your terminal emulator or shell startup file to a real terminal name, such as xterm-256color or screen-256color, then restart Vim. Do not set TERM inside vimrc; it is too late for Vim's terminal initialization.
If the external command reads stdin
Commands such as less, vim, or custom scripts that read from standard input will block because Vim's :! mechanism does not supply a usable terminal by default. Redirect stdin:
:!less file < /dev/null
Risk: this breaks interactive tools that genuinely need input. For interactive workflows, use :terminal instead, available in Vim 8 and later. Neovim handles terminals and job control differently; verify the issue reproduces in Vim before applying Vim-only fixes.
Escalation criteria
Escalate when:
vim --cleanstill freezes on:!echo ok, which suggests a terminal or build issue rather than configuration.- You need an interactive shell such as fish as
shelland cannot makeshellcmdflag,shellpipe, andshellredirwork together. - The freeze happens only inside a terminal multiplexer or remote SSH session, pointing to
TERMor terminal-control-sequence problems outside Vim's scope.
Limitations and how to verify the result
This guide covers Vim, not Neovim. Neovim's built-in terminal and job control differ, so a fix that works in Vim may not apply there. Changing shell changes state: any running Vim session keeps the old value until restarted, and scripts that depend on the previous shell may break. After applying a fix, verify by running :!echo ok and confirming Vim returns to the prompt immediately, then run your original failing command, such as :%!sort, and confirm output appears without hanging.
If the command still hangs after these checks, test the shell invocation manually in your terminal as described above before changing Vim configuration further.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.