aboutsummaryrefslogtreecommitdiff
path: root/src/blog/2026-06-28-tmux-open-urls/tmux-open-urls.md
blob: 4547a5a65b905cf3c47137eff89bc8dfc0ab6c00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# tmux trick #3: open URLs without the mouse

*This post is part of a [series](../../series)*

My [last post in this series](../2026-05-17-tmux-clipboard) on copy mode
was inspired by an old `.tmux.conf` file that I found in a backup folder.
In that same file I also had a command that selects all URLs that are
visible in the current pane, shows them to the user in a
[dmenu](https://tools.suckless.org/dmenu) session, and opens the
selected URL in a browser. This command is bound to `C-b u` with
the following configuration line:

```
bind u run "tmux capture-pane; tmux show-buffer | urlgrep | dmenu -l | xargs firefox"
```

This command is a great example of UNIX program composability. Let's break it down!

## The pipeline

First, `tmux capture-pane` copies all the text of the current pane in
its copy buffer. Then `tmux show-buffer` prints the current copy buffer
to standard output - we talked about this last time.

This text is then [piped](https://en.wikipedia.org/wiki/Pipeline_(Unix))
into `urlgrep`, a custom script that I wrote by copy-pasting a regular
expression from some StackOverflow answer:

```
#!/bin/sh

protocols='http|https|ftp|sftp|gemini|mailto'
valid_chars="][a-zA-Z0-9_~/?#@!$&'()*+=.,;:-"
regex="(($protocols):|www\.)[$valid_chars]+"

egrep -o "$regex"
```

This [grep](../2023-08-20-grep) command selects all URLs from its standard
input and prints them out one per line. These URLs are then sent to dmenu,
a graphical tool that prompts the user to select one of the items it received
in its standard input. The `-l` option is used to change `dmenu`'s layout so
that it shows one option per line.

The user's selection is then printed by `dmenu` to standard output; so we
use `xargs` to convert it to an argument for the `firefox` command. And
that's it! You can now follow links from your terminal using only your keyboard.

## Caveats

If a URL is broken into multiple lines, my `urlgrep` script is only going to
select it until the end of the first line, so you won't be able to open
it correctly with this configuration. However, I found this to be a common
issue in many terminal emulators.

## Update 2026-07-05

Shortly after this post was published, a reader emailed me his modified
version of the binding:

```
bind u {
	capture-pane -J
	display-popup -w 80% -T "Select URL to open" -E "tmux show-buffer | urlview"
}
```

This taught me three things:

1. `urlview` can replace my custom combo of scripts - cool! But I kinda like
   my scripts, I think I'll keep using them.
2. Using `display-popup` to show a "window" inside the tmux session. This can
   be useful e.g. if one is not running a graphical session at all. I can
   use it together with the `-m fzf` option for my `dmenu-urlselect`.
3. The `-J` option for `capture-pane` to preserve trailing spaces and join
   any wrapped lines. This may solve the caveat I mentioned above!

Thanks Johannes!

Generated with cgit - Back to sebastiano.tronto.net