Fuzzy Finder (fzf) is an Absolute Command-Line Powerhouse

Introduction

Over the past year, I’ve dedicated countless hours to developing a unified system which seamlessly integrates tmux and reptyr with various terminal user interface libraries—ranging from rich/textual and PyTermGUI to curses.

Why? Because I recognize the power of the command line. At the same time, I understand that for many, command-line interfaces can be a significant barrier—often due to a lack of understanding, the sheer complexity, or an overwhelming sense of intimidation. Too often, these challenges lead to neglect, whether from a belief that it’s too much effort or simply feeling out of depth.

I believe there exists a kind of great divide—a barrier between those deeply immersed in coding, development, and hacking, and those who might have the potential to contribute but feel locked out. As someone who values the core principles of open source—collaboration, accessibility, and the free exchange of ideas—breaking down that barrier, even just a little, feels like a worthwhile pursuit. Anyone who shares the ideals of open source likely sees the importance of community-driven innovation and making knowledge available to those who seek it.

The unfortunate reality is that countless brilliant minds, capable of making meaningful contributions to the vast ecosystem of open-source projects—past, present, and future—are often excluded, not by lack of interest, but by the sheer difficulty of entry. This isn’t just a hypothetical issue; it’s a missed opportunity. If there were a way to make these tools, workflows, and concepts more approachable, we wouldn’t just be opening doors for new contributors—we’d be unlocking new possibilities for everyone willing to step through.

That’s why I’ve devoted so much of my free time to bridging this gap—creating a system that prioritizes usability without sacrificing the depth and flexibility that power users demand.

Discovery

I disclosed a Broken Access Control bug to Ultimate-Guitar on January 14, 2025. As part of the disclosure, I wrote a shell script, cleverly named EchoHeist, to automate the bug exploitation process which I coined ‘Regex -> Replay -> Redirect. Corny I know.. But the three ‘R’s provide a simple description of the vulnerability at its core.

Unreleased Tool - echoHEIST Unreleased Tool - echoHEIST
My implemenetation of fzf to aid in selecting dymanic scraped data in 'echoHEIST' - a broken access control bug I discovered on Ultimate-Guitar dot com.

READ MORE

I’d be lying in saying my initial focus wasn’t to leverage this bug farther via web scraping and automation. In fact, that is exactly what the second version pictured above did. However its design was to also be user friendly via a simplistic terminal based user interface. This implementation would enable just about anyone to navigate it.

Ultimately, I decided this did not align with the path I’d like to take moving forward. Releasing a tool like this would enable anyone the ability to abuse it in a very efficient and automated way. And while I don’t agree with how UG handles registration pushing, I decided not to publicly release it. It came to me as an epiphany the night I finished writing it. As a result, I disclosed the bug to the website administration and my github.

In the end, what’s important is I found out about fzf by researching (yet again) how I could extend the initial script into something more user-friendly from a user interface perspective. And I have to say, I wish I had stumbled upon fzf sooner

That summarizes what has led me to writing this. It’s why the topics I’ll be discussing involve tmux, reptyr, and fzf. They are all core aspects of my initial goal.
This post discsses some of my experiences while working with tmux, reptyr, and fzf - with a heavy focus on their implementation from a scripted or programmed environment.

Tmux - The Terminal Multiplexor

Tmux Logo
When I discovered tmuxthe terminal multiplexer—it pulled me into a deep rabbit hole where I found myself aimlessly attempting to script my way through panes, windows, and sessions in a variety of terribly unplanned ways. I was fascinated with its simple yet transformative effect on the local terminal.

Naturally, I started exploring the python library libtmux. I was eager to play with this as I learned the tool...

Yet instead of clarity, I found:

  • Outdated documentation
  • Deprecated methods
  • Missing replacements
  • When I first started working with tmux programmatically, I found it particularly difficult managing the session’s child objects (i.e., the panes).

    NOTE: This discussion pertains exclusively to working with tmux programmatically. Once you become comfortable with its keybindings and develop an intuitive understanding, tmux can exponentially boost your terminal productivity.

    Tmux follows an object-oriented hierarchy:

    • A tmux server is parent to sessions.
    • Each session is parent to one or more windows.
    • Each window is parent to one or more panes.

    This structure resembles a tree, with the server as the root node, sessions as branches, and windows and panes as successive child nodes. It is a familiar model for anyone who has worked with an object oriented programming language. However, unlike stack and heap based data structures as an example, which are predictible with respect to adding and removing data, tmux dynamically reassigns numeric identifiers as objects are created or destroyed, and it does this based on the screen position of the pane, making consistent indexing extremely difficult.

    Why Tracking by Index is Problematic

    • In most data structures, indices or positions are persistent unless explicitly changed.
    • In tmux, however, pane indices are purely positional and shift dynamically when panes are added or removed.
    • If you split a window into multiple panes, indices are assigned in order. But when a pane is closed, tmux renumbers the remaining panes, meaning a pane labeled “2” could suddenly become “1”. Depending on its location, this may only change half the pane indices, or one, or all. It is entirely dependent to where the pane is split
    • This makes tracking panes via index unreliable, as they don’t persist over time. Instead, using pane titles or unique names is a more effective approach for keeping track of panes programmatically. More on this to come..

    The indexing issue isn’t exclusive to panes either—it also affects windows and even sessions. However, it becomes particularly significant when dealing with panes, which are central to dynamically modifying the terminal. This might explain why panes have a unique title property, unlike windows and sessions. One might logically assume that renaming a pane would follow a command syntax similar to its parent objects—something like tmux name-pane {name} or even tmux name-title {title}—but that isn’t the case.

    Instead, panes come with their own exclusive title property that functions similarly to a name but has distinct characteristics. Notably, you can only assign a title to a pane when it is currently selected. For example, you can set the title of the active pane using the command:

    1
    tmux select-pane -T "Title"

    This title is then displayed in the status bar, setting it apart from window and session names. When it comes to default pane names (which are based on indexing), maintaining consistency throughout a session becomes challenging. In a 2D split layout—where panes are arranged both horizontally and vertically—sequential indexing can quickly become confusing, particularly as panes are dynamically added or removed. Nevertheless, panes still adhere to the indexing concept, accessible via #{pane_index}, although this number will change as the layout evolves.

    I personally do not find this intuitive with respect to learning or coding in general.

    Experienced tmux scripters will likely challenge my perspective, and I acknowledge that I may be somewhat naive in this regard. I also found using libtmux to handle advanced scenarios just as frustrating, if not moreso. Personally, I the most success by programmatically utilizing key bindings in bash scripts or as subprocess to the aformentioned within python to be most reliable - which is a bit backwards don’t you think?

    For a more visual approach to demonstrating these challenges. And yes, the ‘pain’ typo is intended:

    1. Create a new tmux session named pain_demo.
    2. Split the window into multiple panes in a 2D layout.
    3. Display the current pane indices within each pane.
    4. Dynamically remove and add panes, showcasing how the indices shift.
    >
    Click for Code ExampleHide!

    Code Example

    "[DtRH.net]-pain_demo.sh"
    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
    79
    80
    81
    82
    83
    84
    85

    #!/bin/bash

    # ====================================================
    # Tmux Pain Demonstration
    # ====================================================

    # Name of the tmux session
    SESSION="pain_demo"

    # Check if the session already exists
    tmux has-session -t $SESSION 2>/dev/null

    if [ $? != 0 ]; then
    echo "Creating new tmux session: $SESSION"

    # Start a new tmux session detached with one pane
    tmux new-session -d -s $SESSION -n pain_demo

    # Function to send a command to a specific pane
    send_cmd() {
    # Arguments:
    # $1 - target pane (e.g., pain_demo.0)
    # $2 - command to execute
    tmux send-keys -t "$SESSION:pain_demo.$1" "$2" C-m # <-- More on this later..
    }

    # Initial Pane Setup
    echo "Splitting the window into 4 panes..."

    # Split the initial pane (0) horizontally into pane 1
    tmux split-window -h -t $SESSION:pain_demo.0

    # Split pane 0 vertically into pane 2
    tmux split-window -v -t $SESSION:pain_demo.0

    # Split pane 1 vertically into pane 3
    tmux split-window -v -t $SESSION:pain_demo.1

    # Now, we have 4 panes: 0, 1, 2, 3

    # Assign titles to each pane displaying their initial indices
    send_cmd 0 'echo "Initial Pane Index: 0"; bash'
    send_cmd 1 'echo "Initial Pane Index: 1"; bash'
    send_cmd 2 'echo "Initial Pane Index: 2"; bash'
    send_cmd 3 'echo "Initial Pane Index: 3"; bash'

    echo "Initial pane indices set. You have 10 seconds to observe."
    sleep 10

    # Demonstrate Pane Removal
    echo "Removing pane 1..."
    tmux kill-pane -t "$SESSION:pain_demo.1"

    # After killing pane 1, panes 0, 2, 3 are reindexed to 0,1,2
    echo "Updating pane indices after removal..."
    send_cmd 0 'clear; echo "Updated Pane Index: 0"; bash'
    send_cmd 2 'clear; echo "Updated Pane Index: 1"; bash'
    send_cmd 3 'clear; echo "Updated Pane Index: 2"; bash'

    echo "Pane removal demonstrated. You have 10 seconds to observe."
    sleep 10

    # Demonstrate Pane Addition
    echo "Adding a new pane by splitting pane 0 horizontally..."
    tmux split-window -h -t "$SESSION:pain_demo.0"

    # Now, panes are 0,4,1,2 (before reindexing)
    # tmux will reindex them to 0,1,2,3

    echo "Updating pane indices after addition..."
    send_cmd 0 'clear; echo "Updated Pane Index: 0"; bash'
    send_cmd 4 'clear; echo "Updated Pane Index: 3"; bash'
    send_cmd 2 'clear; echo "Updated Pane Index: 1"; bash'
    send_cmd 3 'clear; echo "Updated Pane Index: 2"; bash'

    echo "Pane addition demonstrated. You have 10 seconds to observe."
    sleep 10

    echo "Demonstration complete. Attaching to tmux session..."
    fi

    # Attach to the tmux session
    tmux attach -t $SESSION


    "Expected Script Output"
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $ ./\[DtRH\]-pain_demo.sh               
    Creating new tmux session: pain_demo
    Splitting the window into 4 panes...
    Initial pane indices set. You have 10 seconds to observe.
    Removing pane 1...
    Updating pane indices after removal...
    can't find pane: 3
    Pane removal demonstrated. You have 10 seconds to observe.
    Adding a new pane by splitting pane 0 horizontally...
    Updating pane indices after addition...
    can't find pane: 4
    Pane addition demonstrated. You have 10 seconds to observe.
    Demonstration complete. Attaching to tmux session...
    sessions should be nested with care, unset $TMUX to force

    Visual Example with ASCII

    1. Initial Layout (4 Panes):

      1
      2
      3
      4
      5
      +-------+-------+
      | Pane0 | Pane1 |
      +-------+-------+
      | Pane2 | Pane3 |
      +-------+-------+
    2. After Removing Pane 1 (3 Panes Reindexed):

      1
      2
      3
      4
      5
      +-------+-------+  # Every pane index has now shifted
      | Pane0 | # ie. Pane2 became pane1, pane3 became pane2
      +-------+-------+ # Pane0 became Pane1 and Pane0
      | Pane1 | Pane2 |
      +-------+-------+
    3. Insert horizontal Pane via Pane0 Split

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      +-------+-------+                     +-------+-------+
      | Pane0 | | Pane0 | Pane1 |
      +---------------+ +-------+-------+
      | Pane1 | ---> | Pane2 |
      +-------+-------+ ---> +-------+-------+
      | Pane2 | Pane3 | | Pane3 | Pane4 |
      +-------+-------+ +-------+-------+

      Introducing a split Splitting Pane 0
      shifts Pane1 and Shifts every Pane
      Pane2. but not Pane0

    Hopefully this illustrates The challenges faced when trying to track panes by index.
    What a pain.

    NOTE: The code snippet below describes only one of many ways to interact with tmux programmatically. This is a relevant conversation piece.

    ‘C-m’ describes a keybind combination - in this case, it is describing the equivalent action to be executed if the user had just hit the ‘ENTER’ key. You likely noticed three parameters are passed, where one and two ($1 and $2) are function parameters, however when it comes time to execute, ‘C-m’ is appended.

    As I mentioned earlier, libtmux isn’t the easiest to use, but scripting with keybinds is a very valid and much more useful method of programattically working with tmux (in my opinion). So this function takes its first parameter, $1=paneId , second parameter $2=command, then finishes with C-m, where ‘C-m’ represents an ‘Enter’ keypress, which sends the command. We could extend this function signifigantly to map any key bind to any functional need. This command effectively sets focus on a pane ($1) via send-keys, types a command into terminal ($2), and executes the command (C-m).

    Bypassing libtmux via native send-keys command
    1
    2
    3
    4
    5
    6
    7
    # Function to send a command to a specific pane
    send_cmd() {
    # Arguments:
    # $1 - target pane (e.g., demo.0)
    # $2 - command to execute
    tmux send-keys -t "$SESSION:demo.$1" "$2" C-m
    }

    What About Reptyr?

    Reptyr describes itself in its GitHub repository as “Reparent a Running Program to a New Terminal.” This functionality can be extremely useful in various scenarios. Real world example:

    You’ve started a long-running process over SSH but need to leave without interrupting it.

    Solution:
    Start a tmux session, use reptyr to grab the running process, and then disconnect your SSH session. When you reconnect later, you can reattach to your tmux session and continue working seamlessly.

    Reptyr offers more than this - in fact, a tmux session is only necessary in the event you are connected via SSH. Locally, you can use reptyr to attach to almost any process, regardless of if tmux is running or not.

    This is perhaps the simplest and most effective way of describinh tmux and reptyr working in unison. Many of us have faced the challenge of needing to leave a remote SSH session while an important task is running. Whether you need to sign off or are dealing with intermittent connection drops—as I have experienced personally—the risk is that even a brief disconnection could terminate your long-running process, forcing you to start over.

    By launching a tmux server, splitting the window into multiple panes, and using reptyr to reparent the process, you ensure that the process continues running locally on the host. Even if your SSH connection drops, the tmux server maintains the session. Later, you can simply SSH back into the host and reattach to the tmux session where your process is safely running.

    Tip: While it is a more 'dated' method of achieving this persistence while remotely connected to a system, you can use 'screen'. This could be handy if you found yourself on a system which doesn't have tmux installed, or you lack the permissions to install or use it.
    screen -dmS [session name] [command]

    FZF - Fuzzy Finder

    Fzf is short for Fuzzy Finder. It’s a command-line tool which shines while filtering large sets of data piped into it, allowing you to filter the data dynamically in real time as oppose to relying on native regular expressions which are extremely difficult for beginners and are open to a high probability of human error. The image below is a simple example used to illustrate a simple fzf use case scenario. When combined in with the ‘rm’ (remove) command, it adds a safeguard to file removal via an interactive component:

    Interactive File Removal with fzf
    1
    ls -l Downloads/ | grep -i '.png' | fzf -m | xargs rm

    fzf interactive file removal

    The command translates into:


    Command Function
    ls -l List all non-hidden files in the ‘./Downloads/‘ folder
    grep -i ‘.png’ Filter out any lines which do not contain the string ‘.png’, effectively returning all files with the ‘.png’ extention. The -i flag states case insensitivity.
    fzf -m Fuzzy find in multi mode.
    xargs rm Anything passing the filtering above will have rm applied to it, effectively removing the file

    fzf filters lists at lightening fast speeds. All the information you could ever need can be found in its documentation, man pages, and help menu –help/-h.

    I’ve done my best to think of clever ways to utilize this tool in combination with the other tools I’ve discussed this far. The scripts to follow should prove to be not only useful but also act as excellent examples of creative implementation methods. They certainly align with my initial goals - providing an easy and dynamic way to implement a user interface for command-line only tools

    I consider these on a more advanced level. The point is, implementing fzf is a walk in the park compared to traditionally hard coding a user interface


    Advanced Implementation of Fuzzy Finder

    Example 1: Log and Event Viewer

    This first example combines fzf with tmux by horizontally splitting the window into two equally sized panes. The top pane finds log files in a typical system log directory (/var/log) and pipes that into fzf for navigation. I've also included an option to also view the latest entries in dmesg.

    The bottom pane is for displaying the log data. When a selection is made in the top pane, its contents (most recent to least recent) is displayed there.

    The script is written in such a way it will not exit until the user issues a sigkill with ctrl+c. As a result, you can very easily check multiple log files without the need to touch a mouse!

    The tmux command 'send-keys' is used action decisions made in the top pane to the bottom pane - be it to output the log data or dmesg STDOUT. I've already discussed this a little bit earlier in this writing. Working the bash subprocess method with tmux seems to be the most effective way of working with it.

    NOTE: I added the dmesg example in there, which is being displayed in the picture below, just to ephasize the simplicity of integrating custom actions. Viewing output from the command 'dmesg' is inherently a different task than viewing the contents of a file. This describes the extensive potential capabilities in a very subtle way.

    Logview
    >
    Click HERE for Python Script CodeHide
    "logview.py"
    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
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    #!/usr/bin/env python3
    """
    Continuous Log Viewer using tmux and fzf

    Features:
    1. Lists system log files (from /var/log) along with a special "dmesg (live)" option.
    2. Opens a dedicated tmux pane to display log output.
    3. Continuously allows you to select a new log file, updating the dedicated pane
    with the output from tailing the selected log file or running live dmesg.
    """

    import os
    import sys
    import subprocess
    import shutil

    LOG_DIR = "/var/log"

    def check_dependencies():
    """Ensure that tmux and fzf are installed."""
    for cmd in ["tmux", "fzf"]:
    if shutil.which(cmd) is None:
    print(f"Error: {cmd} is not installed or not in your PATH.")
    sys.exit(1)

    def get_log_options():
    """
    Build a list of log options:
    - A special entry for live dmesg output.
    - All files found in /var/log.
    """
    options = ["dmesg (live)"]
    if os.path.isdir(LOG_DIR):
    for entry in sorted(os.listdir(LOG_DIR)):
    full_path = os.path.join(LOG_DIR, entry)
    if os.path.isfile(full_path):
    options.append(full_path)
    return options

    def fzf_select(options):
    """
    Use fzf to allow the user to select one of the provided options.

    Returns:
    The selected option as a string, or None if nothing was selected.
    """
    try:
    result = subprocess.run(
    ["fzf", "--prompt=Select log file (or 'dmesg (live)'): "],
    input="\n".join(options),
    text=True,
    capture_output=True,
    check=True
    )
    selection = result.stdout.strip()
    return selection if selection else None
    except subprocess.CalledProcessError:
    return None

    def open_dedicated_pane():
    """
    Create a new tmux pane for log output.

    Returns:
    The tmux pane ID as a string.
    """
    try:
    pane_id = subprocess.check_output(
    ["tmux", "split-window", "-v", "-P", "-F", "#{pane_id}"],
    text=True
    ).strip()
    return pane_id
    except subprocess.CalledProcessError:
    print("Error: Failed to create a new tmux pane.")
    sys.exit(1)

    def send_command_to_pane(pane_id, command):
    """
    Send a new command to the specified tmux pane.

    This function first sends a Ctrl-C to stop any running command,
    then clears the pane and executes the new command.

    Args:
    pane_id: The target tmux pane ID.
    command: The shell command to run in that pane.
    """
    # Send Ctrl-C to ensure any previous command is terminated.
    subprocess.run(["tmux", "send-keys", "-t", pane_id, "C-c"])
    # Clear the pane and run the new command.
    full_command = f"clear; {command}"
    subprocess.run(["tmux", "send-keys", "-t", pane_id, full_command, "Enter"])

    def main():
    check_dependencies()

    # Ensure we're running inside a tmux session.
    if "TMUX" not in os.environ:
    print("Error: This script must be run within a tmux session.")
    sys.exit(1)

    # Create a dedicated pane for log output.
    pane_id = open_dedicated_pane()
    print(f"Dedicated log pane created with ID: {pane_id}")

    # Retrieve the log file options.
    options = get_log_options()

    # Main loop: repeatedly prompt for a new log file selection.
    while True:
    selection = fzf_select(options)
    if not selection:
    print("No selection made. Exiting.")
    break

    # Determine the command based on the selection.
    if selection.startswith("dmesg"):
    cmd = "dmesg -w"
    else:
    if not os.path.exists(selection):
    print(f"Error: Log file not found: {selection}")
    continue
    cmd = f"tail -f {selection}"

    # Send the new command to the dedicated pane.
    send_command_to_pane(pane_id, cmd)
    print(f"Now displaying output from: {selection}")

    if __name__ == "__main__":
    try:
    main()
    except KeyboardInterrupt:
    print("\nExiting...")
    sys.exit(0)


    Example 2: PSHijack

    This one is purely shell script. PS for 'process'. Sometimes, processes can cause unexpected issues on your system. Consider the following scenarios:
    1. Lock Contention:
      A process might be holding a lock in /var/lock, preventing the installation of a program because it’s waiting for input in an unattended window.

    2. Crashed Processes:
      A defunct or crashed process might block the package manager, hindering your ability to install or update software.

    3. Process State Verification:
      You may need to quickly determine whether a process is running (or not) to diagnose issues or enforce constraints.

    To streamline troubleshooting, this script leverages FZF (Fuzzy Finder) to dynamically browse and filter running processes. The interactive nature of FZF means you don’t need to grapple with complex regular expressions, output redirection, or pagers like less to sift through process listings.

    In the event this sort of thing happens, it is incredibly frustrating if you can’t access the process directly (or indirectly). Maybe there is some information you are missing, maybe you just dont know the issue at hand. PSHijack simplifies this by detailing process information as well as offering a simple foolproof way to steal the pty if possible by leveraging the strengths of reptyr and fzf.

    Process Explorer
    >
    [ Click HERE for Shell Script ]Hide
    "PSHijack.sh"
    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
    #!/bin/bash
    # PSHijack.sh
    # An process explorer that uses the native linux command ps with fuzzy finder (fzf) and reptyr to hijack (reattach) a running process into your current terminal.
    #
    # Requirements: ps, fzf, reptyr
    # Usage: ./PSHijack.sh

    # Ensure reptyr is installed.
    if ! command -v reptyr >/dev/null 2>&1; then
    echo "Error: reptyr is not installed. Please install it first."
    exit 1
    fi

    # Define FZF options.
    FZF_OPTS=(
    --header="Process Explorer: Use arrow keys to navigate. Press Enter to select a process."
    --footer="Press Esc to cancel selection."
    --preview-window=down:3:wrap
    --height=50%
    --layout=reverse
    )

    # Use ps to list running processes (sorted by CPU usage descending).
    process_list=$(ps aux --sort=-%cpu)

    # Use FZF to select a process interactively.
    selected_proc=$(echo "$process_list" | fzf "${FZF_OPTS[@]}" --preview="echo 'Process Details:'; echo {}; echo; echo 'TTY:'; ps -p $(echo {} | awk '{print \$2}') -o tty --no-headers" )

    # If no process is selected, exit.
    if [[ -z "$selected_proc" ]]; then
    echo "No process selected. Exiting."
    exit 0
    fi

    # Extract the PID from the selected line via field 2
    pid=$(echo "$selected_proc" | awk '{print $2}')

    if [[ -z "$pid" ]]; then
    echo "Failed to extract PID from the selected process."
    exit 1
    fi

    echo "Selected Process PID: $pid"
    echo "Process details:"
    echo "$selected_proc"
    echo

    # Confirm hijack action.
    read -p "Hijack process $pid into the current terminal? [y/N]: " confirm
    if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
    echo "Hijack aborted."
    exit 0
    fi

    # Display current terminal device.
    tty_device=$(tty)
    echo "Hijacking process $pid into terminal: $tty_device"

    # Attempt to hijack the process using reptyr.
    if sudo reptyr "$pid"; then
    echo "Process $pid successfully hijacked into $(tty)."
    else
    echo "Failed to hijack process $pid. You might need elevated privileges."
    fi

    Example 3: URLSex

    This script will take any input in the form of a proper url (be sure to include http:// or https://) and scan it for href endpoints. It will return the list which you can then dynamically select out of a list.

    This could easily be extended into something great.

    Process Explorer
    >
    Click for Python CodeHide!
    urlsex
    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
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    #!/usr/bin/env python3

    """
    urlsex.py

    This script takes a URL as input, scrapes all the href links from the page,
    sorts them by their file extension, and then presents them via FZF with a
    custom preview pane that displays the guessed file type (using Python’s mimetypes).
    The user can interactively select a URL, which is then printed to stdout.

    Requirements:
    - requests
    - beautifulsoup4
    - fzf (installed on your system)

    Usage:
    python3 url_Sex.py [URL]
    (If no URL is provided as an argument, the script will prompt for one.)

    """

    import sys
    import os
    import subprocess
    import mimetypes
    from urllib.parse import urljoin, urlparse

    import requests
    from bs4 import BeautifulSoup


    def scrape_hrefs(url):
    """Fetch the URL and scrape all href attributes from <a> tags."""
    try: response = requests.get(url)
    response.raise_for_status()
    except Exception as e:
    print("Error fetching URL:", e)
    sys.exit(1)
    soup = BeautifulSoup(response.text, "html.parser")
    hrefs = set()
    for a in soup.find_all("a", href=True):
    # Convert relative URLs to absolute using urljoin
    full_url = urljoin(url, a["href"])
    hrefs.add(full_url)
    return list(hrefs)


    def sort_by_extension(urls):
    """Sort URLs by their file extension (case-insensitive)."""
    def ext_key(url):
    path = urlparse(url).path
    _, ext = os.path.splitext(path)
    return ext.lower()
    return sorted(urls, key=ext_key)


    def run_fzf(urls):
    """
    Run FZF with custom options.
    The preview command runs a Python one-liner that prints the guessed MIME type.
    """
    # Build a preview command that takes the highlighted URL as {}.
    # The command uses Python's mimetypes.guess_type to display the file type.
    preview_cmd = (
    "python3 -c \"import sys, mimetypes; "
    "u=sys.argv[1]; "
    "ft = mimetypes.guess_type(u)[0] or 'Unknown'; "
    "print('File type:', ft)\" {}"
    )
    fzf_opts = [
    "fzf",
    "--height=40%",
    "--layout=reverse",
    "--header=Select a URL from the list:",
    "--footer=Press ESC to cancel.",
    "--preview", preview_cmd
    ]
    try:
    proc = subprocess.Popen(
    fzf_opts,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    text=True
    )
    input_data = "\n".join(urls)
    stdout, _ = proc.communicate(input=input_data)
    if proc.returncode != 0:
    return None
    return stdout.strip()
    except Exception as e:
    print("Error running fzf:", e)
    sys.exit(1)


    def main():
    if len(sys.argv) < 2:
    url = input("Enter URL: ").strip()
    else:
    url = sys.argv[1]
    hrefs = scrape_hrefs(url)
    if not hrefs:
    print("No hrefs found on the page.")
    sys.exit(0)
    sorted_hrefs = sort_by_extension(hrefs)
    selected = run_fzf(sorted_hrefs)
    if selected:
    print("Selected URL:", selected)
    else:
    print("No selection made.")


    if __name__ == "__main__":
    main()

    Downloads

    File Name Language Integrations Description
    Example 1: logview.sh Python tmux, fzf System log and event viewer
    Example 2: pshijack.sh Shell tmux, reptyr, fzf View all processes dynamically/hijack one to your current terminal
    Example 3: urlsex.py Python fzf Scrape all links from a given url and perform a task on ones you select

    References & Resources

    Tool Website
    Fuzzy Finder https://github.com/junegunn/fzf
    Tmux https://github.com/tmux/tmux
    Reptyr https://github.com/nelhage/reptyr
    rich https://github.com/Textualize/rich
    curses https://docs.python.org/3/howto/curses.html

    KBS < admin [AT] dtrh [DOT] net >