{"id":13502870,"url":"https://github.com/fritzrehde/watchbind","last_synced_at":"2025-06-20T12:33:34.813Z","repository":{"id":59002048,"uuid":"487234399","full_name":"fritzrehde/watchbind","owner":"fritzrehde","description":"A cli menu for periodically watching a program's output and executing commands on its lines through keybindings","archived":false,"fork":false,"pushed_at":"2024-05-25T06:54:02.000Z","size":625,"stargazers_count":24,"open_issues_count":27,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-18T20:05:39.304Z","etag":null,"topics":["cli","command-line","rust","scripting","terminal","tui","watch"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fritzrehde.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-30T09:23:20.000Z","updated_at":"2025-04-11T23:33:36.000Z","dependencies_parsed_at":"2023-10-31T12:31:03.321Z","dependency_job_id":"220fee0b-7369-4ec7-bd45-8f69572c095b","html_url":"https://github.com/fritzrehde/watchbind","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/fritzrehde/watchbind","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fritzrehde%2Fwatchbind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fritzrehde%2Fwatchbind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fritzrehde%2Fwatchbind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fritzrehde%2Fwatchbind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fritzrehde","download_url":"https://codeload.github.com/fritzrehde/watchbind/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fritzrehde%2Fwatchbind/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260944913,"owners_count":23086913,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","command-line","rust","scripting","terminal","tui","watch"],"created_at":"2024-07-31T22:02:27.793Z","updated_at":"2025-06-20T12:33:29.798Z","avatar_url":"https://github.com/fritzrehde.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# Watchbind\n\n[![Build status](https://github.com/fritzrehde/watchbind/actions/workflows/ci.yml/badge.svg)](https://github.com/fritzrehde/watchbind/actions)\n[![Releases](https://img.shields.io/github/v/release/fritzrehde/watchbind?logo=GitHub)](https://github.com/fritzrehde/watchbind/releases)\n[![Crates.io](https://img.shields.io/crates/v/watchbind?logo=Rust)](https://crates.io/crates/watchbind)\n\n*Turn any shell command into a powerful TUI with custom keybindings.*\n\n![screenshot](https://raw.githubusercontent.com/fritzrehde/i/master/watchbind/screenshot-light.png#gh-light-mode-only)\n![screenshot](https://raw.githubusercontent.com/fritzrehde/i/master/watchbind/screenshot-dark.png#gh-dark-mode-only)\n\n\n## Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n- [How it works](#how-it-works)\n- [Configuration](#configuration)\n  - [Keybindings](#keybindings)\n  - [Styling](#styling)\n  - [Formatting with Field Separators and Field Selections](#formatting-with-field-separators-and-field-selections)\n  - [State Management](#state-management)\n- [Tips](#tips)\n\n\n## Features\n\n- **Customizability**: All keybindings and styles (colors and boldness) are customizable.\n- **Flexibility**: Settings can be configured through CLI arguments, a local TOML config file, a global TOML config file, or a combination of these.\n- **Speed**: Written in asynchronous Rust with [Tokio](https://tokio.rs/).\n\n\n## Installation\n\n### From binaries\n\nThe [releases page](https://github.com/fritzrehde/watchbind/releases) contains pre-compiled binaries for Linux, macOS and Windows.\n\n### From [crates.io](https://crates.io/crates/watchbind)\n\n```sh\ncargo install watchbind\n```\n\nOr install the latest git version from the `main` branch:\n```sh\ncargo install --git https://github.com/fritzrehde/watchbind --branch main \n```\n\n### Distro Packages\n\n[![Packaging status](https://repology.org/badge/vertical-allrepos/watchbind.svg)](https://repology.org/project/watchbind/versions)\n\n#### Arch Linux\n\n`watchbind` can be installed from the [extra repository](https://archlinux.org/packages/extra/x86_64/watchbind) using [pacman](https://wiki.archlinux.org/title/Pacman):\n\n```sh\npacman -S watchbind\n```\n\n#### Alpine Linux\n\n`watchbind` is available for [Alpine Edge](https://pkgs.alpinelinux.org/packages?name=watchbind\u0026branch=edge). It can be installed via [apk](https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper) after enabling the [testing repository](https://wiki.alpinelinux.org/wiki/Repositories).\n\n```sh\napk add watchbind\n```\n\n\n## How it works\n\nWatchbind is a command-line tool that aims to help you build custom TUIs from static CLI commands very easily.\nIt works by specifying a \"watched command\" that outputs some lines to stdout that you want to observe.\nThen, we make this static output **dynamic** by re-executing it at a specified watch rate, and we make the TUI **interactive** through custom keybindings that can operate on the individual output lines.\n\n\n## Configuration\n\nThere are several ways to configure watchbind's settings:\n1. **CLI arguments** (see `watchbind -h` for all available arguments).\n2. A **local TOML config file**, specified with `watchbind --local-config-file \u003cFILE\u003e`.\n3. A **global TOML config file**, located either in the user-specified `WATCHBIND_CONFIG_DIR` environment variable or in the default config directory (see `watchbind -h` for the OS-specific default config directory), is loaded automatically if it exists.\nThe difference between the local and global config files is that the latter must not be specified with `--local-config-file \u003cFILE\u003e` on every watchbind invocation, making it convenient for global settings that should apply to all watchbind invocations.\n\nAll configuration ways can be used at the same time, and `watchbind` will determine which settings to use according to the following configuration hierarchy:\n```\nCLI arguments \u003e local TOML config file \u003e global TOML config file\n```\nwhere `a \u003e b` means: If the config setting `X` is configured in both `a` and `b`, the value of `X` from `a` is used.\n\n\n### Keybindings\n\n#### Via Command-Line Arguments\n\nOn the command line, you can specify a comma-separated list of keybindings, where each keybinding is in the format `KEY:OP[+OP]*`.\nOne `KEY` can be bound to multiple operations, therefore, the syntax for each list of operations (`OPS`) is `OP[+OP]*`.\nThe operations are separated by `+` and executed in succession (one after the other).\n\n**TLDR**:\n- Individual keybindings are separated by `,`\n- A keybinding is a pair of key and (multiple) operations separated by `:`\n- Multiple operations are separated by `+`\n\n#### Via TOML Config File\n\nIn a TOML config file, there are several different ways you can specify keybindings:\n```toml\n[keybindings]\n# Single operation, without description\n\"KEY\" = \"OP\"\n\n# Single operation, with description\n\"KEY\" = { description = \"DESC\", operations = \"OP\" }\n\n# Multiple operations, without description\n\"KEY\" = [ \"OP_1\", \"OP_2\", \"OP_3\" ]\n\n# Multiple operations, with description\n\"KEY\" = { description = \"DESC\", operations = [ \"OP_1\", \"OP_2\", \"OP_3\" ] }\n```\n\nThis syntax differs from the command-line syntax because using the TOML array feature is more expressive and more native to the TOML file format.\nFurthermore, this allows you to use the `+` character in your commands, which is not possible with the CLI arguments (since that character is interpreted as a separator).\nIt also doesn't require escaping shell specific characters like `$` (read more about subshell behaviour [here](#subshell)).\n\nYou can find some keybinding examples in the [`examples/`](examples/) directory.\n\n#### Keys\n\nAll supported `KEY` values:\n```\n\u003cMODIFIER\u003e+\u003cCODE\u003e\n\u003cCODE\u003e\n```\n\nAll supported `MODIFIER` values:\n```\nalt\nctrl\n```\n\nAll supported `CODE` values:\n```\nesc\nenter\nleft\nright\nup\ndown\nhome\nend\npageup\npagedown\nbacktab\nbackspace\ndel\ndelete\ninsert\nins\nf1\nf2\nf3\nf4\nf5\nf6\nf7\nf8\nf9\nf10\nf11\nf12\nspace\ntab\n\u003cany single character\u003e\n```\n\n#### Operations\n\n\u003c!-- TODO: Make table of toml config reference name,example,description --\u003e\n\nAll supported `OP` values:\n\nOperation | Description\n:-- | :--\n`exit` | Quit watchbind.\n`reload` | Reload the watched command manually, resets interval timer.\n`cursor [down\\|up] \u003cN\u003e` | Move cursor \\[down\\|up\\] N number of lines.\n`cursor [first\\|last]` | Move cursor to the \\[first\\|last\\] line.\n`select` | Select line that cursor is currently on (i.e. add line that cursor is currently on to selected lines).\n`unselect` | Unselect line that cursor is currently on.\n`toggle-selection` | Toggle selection of line that cursor is currently on.\n`select-all` | Select all lines.\n`unselect-all` | Unselect all currently selected lines.\n`exec -- \u003cCMD\u003e` | Execute `CMD` and block until termination.\n`exec \u0026 -- \u003cCMD\u003e` | Execute `CMD` as background process, i.e. don't block until command terminates.\n`exec tui -- \u003cTUI-CMD\u003e` | Execute a `TUI-CMD` that spawns a TUI (e.g. text editor). Watchbind's own TUI is replaced with `TUI-CMD`'s TUI until `TUI-CMD` terminates. Note that `TUI-CMD` must spawn a full-screen TUI that covers the entire terminal, otherwise undefined behaviour will ensue.\n`set-env \u003cENV\u003e -- \u003cCMD\u003e` | Blockingly execute `CMD`, and save its output to the environment variable `ENV`.\n`unset-env \u003cENV\u003e -- \u003cCMD\u003e` | Unsets environment variable `ENV`.\n`help-[show\\|hide\\|toggle]` | \\[Show\\|Hide\\|Toggle the visibility of\\] the help menu.\n\nAll `CMD` and `TUI-CMD` shell commands will be executed in a subshell (i.e. `sh -c \"CMD\"`) that has some environment variables set.\nThe environment variable `$line` is set to the line the cursor is on.\nThe environment variable `$lines` set to all selected lines, or if none are selected, the line the cursor is currently on.\nAll set environment variables `ENV` will be made available in all future spawned commands/processes, including the watched command, any executed subcommands, as well as commands executed in `set-env` operations.\nIf multiple lines are selected, they will be separated by newlines in `$lines`.\n\n### Styling\n\nForeground colors, background colors and boldness can be customized.\nThese styling options are available for:\n- The line the cursor is currently on with `cursor-[fg|bg|boldness]`.\n- The header lines with `header-[fg|bg|boldness]`.\n- All other lines with `non-cursor-non-header-[fg|bg|boldness]`.\n- The selection indicator with `selected-bg`.\n\nThe names of the customization fields from the command-line options (e.g. `--cursor-fg blue`) are the same in the TOML config file (e.g. `cursor-fg = \"blue\"`).\n\nFurthermore, `watchbind` also supports styling according to ANSI codes in the input text.\n\nAll supported `COLOR` values:\n```sh\nwhite\nblack\nred\ngreen\nyellow\nblue\nmagenta\ncyan\ngray\ndark_gray\nlight_red\nlight_green\nlight_yellow\nlight_blue\nlight_magenta\nlight_cyan\nreset        # Reset the fg and bg\nunspecified  # Don't applying any styling =\u003e use style from ANSI input text\n```\n\nAll supported `BOLDNESS` values:\n```sh\nbold         # Make everything bold\nnon-bold     # Make sure nothing is bold (i.e. remove any bold styling from input ANSI)\nunspecified  # Don't applying any styling =\u003e use style from ANSI input text\n```\n\n### Formatting with Field Separators and Field Selections\n\n`watchbind` supports some extra formatting features reminiscent of the Unix `cut` command:\n\n- **Field Separators**:\nDefine a separator/delimiter to segment your command's output into distinct fields.\nEach separator will be replaced with an [elastic tabstop](https://nick-gravgaard.com/elastic-tabstops/), resulting in a \"table\"-like structure, similar to the `cut -d \u003cSEPARATOR\u003e -t` command.\n\n- **Field Selections**:\nChoose only specific fields to display.\nYou can specify a comma-separated list of the indexes (starting at index 1) for individual fields (`X`), ranges (`X-Y`), or the capture of all fields from X onwards (`X-`).\nFor instance, the field selection `1,3-4,6-` will display the first, third and fourth fields, as well as all fields from the sixth onwards.\n\n**Important**: The `$lines` passed to the `exec --` operations will remain unformatted, i.e. will not have the separators replaced with elastic tabstops and will not have non-selected fields ommitted.\n\n### State management\n\nThe `set-env` and `unset-env` operations allow you to manage state through environment variables.\nAdditionally, you can use the `initial-env` option to specify a list of `set-env` commands that will be executed **before** the first execution of the watched command.\nThis powerful combination allows you to set some initial state with `initial-env`, reference that state directly in the watched command, and update the state with keybindings at runtime with `set-env`.\n\n### Help menu\n\nWatchbind supports a help menu that displays:\n- All environment variables set by `set-env` commands along with their values.\n- All keybindings along with the operations and the description they are mapped to, though you can customize what exactly gets displayed with `--keybindings-help-menu-format`.\n\n\n## Tips\n\n### Keybindings on selected lines that delete some of the input lines\n\nI define \"deleting input lines\" as executing a keybinding that changes the length of the watched command's output.\nIn other words:\nIf, after executing a keybinding, the watched command generates an output longer or shorter than before the keybinding, then that keybinding deletes input lines.\n\nWhy is this definition important?\nBecause the selected lines are only stored as indices and, therefore, have no association to the actual lines displayed in watchbind.\n\nHere's an example that demonstrates what problems this can cause:\nYou select five lines and then, through a keybinding, execute a command that deletes these five lines.\nThe next time your watched command is executed, it will output five lines less (that are displayed in watchbind), since the five lines have been deleted.\nThe problem is that the indices of the deleted lines will still be marked as selected.\nTherefore, five different lines, at the same indices as the deleted five lines, will now be selected, which is probably unwanted.\n\nTo solve this problem, the following keybinding format is recommended for keybindings that transform the input:\n```toml\n[keybindings]\n\"KEY\" = [ \"exec -- DELETE-OP\", \"reload\", \"unselect-all\" ]\n```\n\nFirst, the selected lines are deleted using the `DELETE-OP` (e.g. `echo $lines | xargs rm`).\nThen, we want to see the updated output of the watched command that doesn't contain the deleted lines anymore, so we `reload`.\nFinally, we want to remove our the selection of the now removed lines, so we call `unselect-all`.\n\n### Piping\n\nIf you want to use pipes in your watched command on the command-line, make sure to escape the pipe symbol like so:\n```sh\nwatchbind ls \\| grep \"test\"\n```\nor put quotes around the watched command\n```sh\nwatchbind \"ls | grep test\"\n```\nOtherwise, the shell will think you want to pipe the output of `watchbind exec -- ls` to `grep test`.\n\n### Subshell\n\nThe commands you bind to keys will be executed in a subshell using `sh -c`.\n\nThis means you can run a command like \n```sh\nwatchbind --bind \"enter:notify-send \\$lines\" ls\n```\nand the environment variable `$lines` will contain the line the cursor is currently on.\n\nBut note that \n```sh\nwatchbind --bind \"enter:notify-send $lines\" ls\n```\nwill not work as expected, because `$lines` will be replaced in the shell you are running the `watchbind` command from.\n\n### UI updates while blocking\n\nWatchbind can enter blocking states when a blocking subcommand is executed.\nThe default behaviour in a blocking state is to *not* display any new output lines received from the watched command.\nHowever, this behaviour can easily be customized with the `--update-ui-while-blocking \u003cBOOL\u003e` option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffritzrehde%2Fwatchbind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffritzrehde%2Fwatchbind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffritzrehde%2Fwatchbind/lists"}