0

When I select some text in tmux copy mode, can I invoke system command on the selected text, without using a predefined keybinding?

For example, I could invoke pastebin to send selected text to pastebin, or invoke wc to count lines. I tried following method, but it didn't work:

  1. select text by c-b [
  2. Tried both these ways:
    1. press :, hoping to get into command mode - didn't work because this prompts for "go to line"
    2. press c-b :copy-pipe wc - didn't work because it said copy-pipe is unknown command

Note: I'm using Tmux 3.1c

I'm aware of solutions like this one. However, that'd require me to foresee each of my use cases and predefine the keybinding for each of possible commands I will execute.

In Vim, this is supported, i.e., select text, then :!<system_command> would send the text to system_command.

KFL
  • 269

2 Answers2

2

You're close; well sort-of. Copy-mode commands need to be sent via sendkeys -X.

So to pipe the selected text to a command, it would be:

  • Prefix :
  • send-keys -X copy-pipe "wc"

BUT, Tmux doesn't display the output of a copy-pipe anywhere. With run-command, the output is displayed in a copy-buffer. For copy-pipe, it seems to just be gobbled. The example you linked to in another answer simply saved a buffer, so no output/feedback was necessary.

For a wc, that's not too useful. And I'm guessing that the output of pastebin is a quite-useful URL ...

The simplest alternative that I've found starts to get messy, especially since you want to this with arbitrary commands:

  • Prefix :
  • send-keys -X copy-selection-no-clear ; run-shell "tmux show-buffer | wc"

Note that there's no error checking there to determine if something is selected. It will simply run show-buffer on the most recent buffer.

A Potentially Better Option, IMHO

Since Vim is good at this use-case, use it. Set up a key-binding that opens the scrollback buffer in vim using process substitution.

For testing in the shell (tested):

tmux bind-key -T copy-mode v new-window -d -n scrollback "vi <(tmux capture-pane -p -S - -E -)" \\\; run-shell "sleep 1" \\\; select-window -t scrollback

Or in your config (untested):

bind-key -T copy-mode v new-window -d -n scrollback "vi <(tmux capture-pane -p -S - -E -)" \; run-shell "sleep 1" \; select-window -t scrollback

Hacky, yes, and you may need to increase the sleep for larger buffers -- I haven't tested that. There's probably a better way, but that's what I came up with.

Also you'll lose ANSI color codes, but you might be able to handle that (if you care) with the AnsiEsc VIM extension, along with adding -e to the capture-buffer (to keep ANSI codes in the buffer output).

NotTheDr01ds
  • 3,547
1

Based on NotTheDr01ds answer, I got a full solution as:

bind-key -T copy-mode   !  command-prompt -p "cmd:" "send-keys -X copy-selection-no-clear \; run-shell \"tmux show-buffer | %1\" "
bind-key -T copy-mode-vi   !  command-prompt -p "cmd:" "send-keys -X copy-selection-no-clear \; run-shell \"tmux show-buffer | %1\" "

Usage: select some text then press !. It will prompt you to enter a shell command to run. The selected text will be feed to the shell command as stdin. The stdout of the shell command will be displayed back to the tmux pane.

Thanks NotTheDr01ds for the excellent hint!

KFL
  • 269