sebastiano.tronto.net

Source files and build scripts for my personal website
git clone https://git.tronto.net/sebastiano.tronto.net
Download | Log | Files | Refs | README

tmux-open-urls.md (2899B)


      1 # tmux trick #3: open URLs without the mouse
      2 
      3 *This post is part of a [series](../../series)*
      4 
      5 My [last post in this series](../2026-05-17-tmux-clipboard) on copy mode
      6 was inspired by an old `.tmux.conf` file that I found in a backup folder.
      7 In that same file I also had a command that selects all URLs that are
      8 visible in the current pane, shows them to the user in a
      9 [dmenu](https://tools.suckless.org/dmenu) session, and opens the
     10 selected URL in a browser. This command is bound to `C-b u` with
     11 the following configuration line:
     12 
     13 ```
     14 bind u run "tmux capture-pane; tmux show-buffer | urlgrep | dmenu -l | xargs firefox"
     15 ```
     16 
     17 This command is a great example of UNIX program composability. Let's break it down!
     18 
     19 ## The pipeline
     20 
     21 First, `tmux capture-pane` copies all the text of the current pane in
     22 its copy buffer. Then `tmux show-buffer` prints the current copy buffer
     23 to standard output - we talked about this last time.
     24 
     25 This text is then [piped](https://en.wikipedia.org/wiki/Pipeline_(Unix))
     26 into `urlgrep`, a custom script that I wrote by copy-pasting a regular
     27 expression from some StackOverflow answer:
     28 
     29 ```
     30 #!/bin/sh
     31 
     32 protocols='http|https|ftp|sftp|gemini|mailto'
     33 valid_chars="][a-zA-Z0-9_~/?#@!$&'()*+=.,;:-"
     34 regex="(($protocols):|www\.)[$valid_chars]+"
     35 
     36 egrep -o "$regex"
     37 ```
     38 
     39 This [grep](../2023-08-20-grep) command selects all URLs from its standard
     40 input and prints them out one per line. These URLs are then sent to dmenu,
     41 a graphical tool that prompts the user to select one of the items it received
     42 in its standard input. The `-l` option is used to change `dmenu`'s layout so
     43 that it shows one option per line.
     44 
     45 The user's selection is then printed by `dmenu` to standard output; so we
     46 use `xargs` to convert it to an argument for the `firefox` command. And
     47 that's it! You can now follow links from your terminal using only your keyboard.
     48 
     49 ## Caveats
     50 
     51 If a URL is broken into multiple lines, my `urlgrep` script is only going to
     52 select it until the end of the first line, so you won't be able to open
     53 it correctly with this configuration. However, I found this to be a common
     54 issue in many terminal emulators.
     55 
     56 ## Update 2026-07-05
     57 
     58 Shortly after this post was published, a reader emailed me his modified
     59 version of the binding:
     60 
     61 ```
     62 bind u {
     63 	capture-pane -J
     64 	display-popup -w 80% -T "Select URL to open" -E "tmux show-buffer | urlview"
     65 }
     66 ```
     67 
     68 This taught me three things:
     69 
     70 1. `urlview` can replace my custom combo of scripts - cool! But I kinda like
     71    my scripts, I think I'll keep using them.
     72 2. Using `display-popup` to show a "window" inside the tmux session. This can
     73    be useful e.g. if one is not running a graphical session at all. I can
     74    use it together with the `-m fzf` option for my `dmenu-urlselect`.
     75 3. The `-J` option for `capture-pane` to preserve trailing spaces and join
     76    any wrapped lines. This may solve the caveat I mentioned above!
     77 
     78 Thanks Johannes!