Skip Navigation
Thoughts, tips, and customization of fzf for old-school CLI enjoyers?

Hey,

As an avid CLI user, I always aimed to master non-interactive tools to perform most of my work, given that they are easy to use, create, extend, and connect.

However, I found myself dealing with software projects with many files (mostly under the yoke of corporate oppression; an ordeal which I endure to sustain myself, as most of those reading me do, and therefore I will not go further into this topic) and started to hit the limits of non-interactive tools to find and edit files. Indeed, I could go faster if I followed the temptation of monstrous IDEs, as I did in my innocent past.

I did not despair, as naturally I heard of the usefulness of interactive fuzzy finders such as fzf. After spending an afternoon evaluating the tool, I concluded that it indeed increases the complexity of my workflow. Still, this complexity is managed in a sensible way that follows the UNIX tradition.

I now ask you two general questions:

  • Did you reach similar conclusions to me and decide to use interactive fuzzy finders to solve working on software projects with many files?
  • If you use fzf or similar tools, what can you tell me about your workflow? Any other third-party tools? Do you integrate it into your scripts? Any advice that you can give me out of a long time of experience using the tool that is not easily conveyed by the documentation?

I also ask this very specific question:

  • The one part of fzf which I found missing was a way to interact with the results of grep, and to automatically place the selected file(s) in the prompt or an editor. For that, I created the following two commands. Do you have a similar workflow when you want to bring the speed of fuzzy finding to grep?

```bash #! /usr/bin/env bash

gf: grep + fzf

basically a wrapper for 'grep <ARGS> | fzf | cut -f 1 -d:'

print usage on -h/--help

if [[ "$1" == "-h" || "$1" == "--help" ]]; then echo "Usage: gf <grep-args>" echo echo "~~~ that feel when no 'gf' ~~~" echo echo "- Basically a wrapper for 'grep <ARGS> | fzf | cut -f 1 -d:'" echo "- Opens fzf with grep results, and prints the selected filename(s)" echo "- Note: As this is meant to search files, it already adds the -r flag" echo echo "Example:" echo " $ nvim \gf foobar\" echo " $ gf foobar | xargs nvim" exit 0 fi

run grep with arguments, pipe to fzf, and print the filename(s) selected

custom_grep () { grep -E --color=always --binary-files=without-match --recursive "$@" } remove_color () { sed -E 's/\x1b\[[0-9;]*[mK]//g' } custom_fzf () { fzf --ansi --height ~98% } grep_output=$(custom_grep "$@") if [[ "$?" -ne 0 ]]; then exit 1 else echo "$grep_output" | custom_fzf | remove_color | cut -f 1 -d: fi ```

```bash #! /usr/bin/env bash

ge: grep + fzf + editor

basically a wrapper for 'grep <ARGS> | fzf | cut -f 1 -d: | $EDITOR'

print usage on -h/--help

if [[ "$1" == "-h" || "$1" == "--help" ]]; then echo "Usage: ge <grep-args>" echo echo "- Basically a wrapper for 'grep <ARGS> | fzf | cut -f 1 -d: | \$EDITOR'" echo "- Opens fzf with grep results, and edits the selected file(s)" echo "- Note: As this is meant to search files, it already adds the -r flag" echo "- Note: Internally, it uses the 'gf' command" echo echo "Example:" echo " $ ge foobar" exit 0 fi

takes output from 'gf' and opens it in $EDITOR

grep_fzf_output=$(gf "$@") if [[ -n "$grep_fzf_output" ]]; then $EDITOR "$grep_fzf_output" fi ```

Have a wonderful day, you CLI cowboys.

5
Beachpatrol: A CLI tool to replace and automate your everyday web browser (Now with Firefox support)
  • Unlike a password manager that just logs you in, Beachpatrol can run any automation task, like checking your email, downloading files, or filling out forms. You have to create Playwright scripts for these tasks and run them from a shell command. There is an example script already in the commands folder, which you can run with the command beackmsg smoke-test. The sky is the limit, basically.

  • Beachpatrol: A CLI tool to replace and automate your everyday web browser (Now with Firefox support)
  • Cool project! I'll check it out.

    Regarding userscripting, from the F.A.Q.:

    Why use an external automation tool (Playwright) instead of a browser extension?

    While Beachpatrol allows to control the browser from both the OS and from a browser extension, our priority was the OS. Therefore, something like Playwright was the natural choice.

    Furthermore, while controlling the browser from an extensions is possible, Manifest v3 removed the ability to execute third-party strings of code. Popular automation extensions like Greasemonkey and Tampermonkey could also be affected by Manifest v3. The alternative is to embed the code into the extension, but that would requires re-bundling the extensions after every change. Other tricks do exist to make this approach work, and there is some hope for future Manifest v3 solutions, but this path is certainly tricky.

    It is more likely that Selenium and related tools will continue to work in the foreseeable future given the business demand for traditional browser testing.

  • PM-JESUS: "Your own, package-manager, Jesus" 🎶 (Package Manager front-end)
  • Makes sense and you're probably right, but I'll tell you why I didn't do it that way:

    1. I just did what came first to me
    2. I like the idea of the API defining the project structure
    3. When adding a new package manager, if that ever happens, I would like to see all other implementation of the same functionality on the same file, for help and inspiration
  • PM-JESUS: "Your own, package-manager, Jesus" 🎶 (Package Manager front-end)
  • Tbh these scripts are for my personal use, written in the way that makes sense for me. I only open sourced it as a joke an as an example of how reinventing your own wheel is not that hard sometimes, and comes with the benefit of doing just what you need it to do.

    Actually I was thinking of adding a sysget fallback, as I might need to do some debian/fedora hacking soon.

  • YAS-QWIN (Yet Another SQL-Query Writing Interface)
    github.com GitHub - sebastiancarlos/yas-qwin: YAS-QWIN (Yet Another SQL-Query Writing Interface)

    YAS-QWIN (Yet Another SQL-Query Writing Interface) - GitHub - sebastiancarlos/yas-qwin: YAS-QWIN (Yet Another SQL-Query Writing Interface)

    GitHub - sebastiancarlos/yas-qwin: YAS-QWIN (Yet Another SQL-Query Writing Interface)
    0
    Beachpatrol: A CLI tool to replace and automate your everyday web browser (Wayland support)
  • It should be pretty soon. I've got it working already, but I need to test it more and figure out how Firefox profiles work with Playwright.

    If you want you can just clone it and replace "chromium" with "firefox". It should just work, and it shouldn't take too long to figure out the rest.

  • Beachpatrol: A CLI tool to replace and automate your everyday web browser (Wayland support)
    github.com GitHub - sebastiancarlos/beachpatrol: A CLI tool meant to replace and automate your everyday web browser.

    A CLI tool meant to replace and automate your everyday web browser. - GitHub - sebastiancarlos/beachpatrol: A CLI tool meant to replace and automate your everyday web browser.

    GitHub - sebastiancarlos/beachpatrol: A CLI tool meant to replace and automate your everyday web browser.
    12
    Microsoft builds a Death Ray.
    sebastiancarlos.com Microsoft builds a Death Ray.

    In a stunning turn of events, Microsoft has developed a device that experts are calling the “death ray.”

    Microsoft builds a Death Ray.
    5
    Sway-Talisman: Sway – Terminal Application Launcher in Scratchpad, Minimalist And Native
    github.com GitHub - sebastiancarlos/sway-talisman: Sway-Talisman: Terminal Application Launcher in Scratchpad, Minimalist And Native

    Sway-Talisman: Terminal Application Launcher in Scratchpad, Minimalist And Native - GitHub - sebastiancarlos/sway-talisman: Sway-Talisman: Terminal Application Launcher in Scratchpad, Minimalist An...

    GitHub - sebastiancarlos/sway-talisman: Sway-Talisman: Terminal Application Launcher in Scratchpad, Minimalist And Native
    1
    Sway-MÜSLI: Sway – Minimal Ültrafast Status Line
    github.com GitHub - sebastiancarlos/sway-musli: Sway-MÜSLI: Sway - Minimal Ültrafast Status LIne

    Sway-MÜSLI: Sway - Minimal Ültrafast Status LIne. Contribute to sebastiancarlos/sway-musli development by creating an account on GitHub.

    GitHub - sebastiancarlos/sway-musli: Sway-MÜSLI: Sway - Minimal Ültrafast Status LIne
    8
    The future is now
  • The joke is that it's hard to tell if this is a joke because the lines between good intentions, corporate jargon, and feasibility have been blurred beyond recognition both here and in the real world.

    It's also funny that after all these years, i18n is still a mess. Moreover, even if translations are standard in GUIs and documentation, for some reason, everyone is okay with defaulting to English for the oldest form of computer interaction.

    Also, the joke is whatever you want it to be. Follow your dreams.

  • Run assembly scripts from your terminal without compiling

    Source code:

    ``` #!/usr/bin/env bash

    runasm - Assemble, link, and run multiple assembly files, then delete them.

    if [[ $# -eq 0 ]]; then echo "Usage: runasm [ ...]" echo " - Assemble, link, and run multiple assembly files, then delete them." echo " - Name of executable is the name of the first file without extension." exit 1 fi

    object_files=() executable_file=${1%.*}

    for assembly_file in "$@"; do # Avengers, assemble! object_file="${assembly_file%.*}.o" as "${assembly_file}" -o "${object_file}" if [[ $? -ne 0 ]]; then exit 1 fi object_files+=("${object_file}") done

    Link

    ld "${object_files[@]}" -o "${executable_file}" if [[ $? -ne 0 ]]; then exit 1 fi

    Run, remove created files, and return exit code

    ./"${executable_file}" exit_code=$? rm "${object_files[@]}" "${executable_file}" > /dev/null 2>&amp;1 exit "${exit_code}" ```

    10
    [Question] Which shell prompt do you use and why?
  • .bashrc:

    # Prompt
    # "Make it simple, just the dollar sign"
    # "Say no more, fam"
    # - if error code is not 0, then prepend [N] where N is the error code
    # - if user is root, use red and #
    blue='\e[34m'
    red='\e[31m'
    bold='\e[1m'
    reset='\e[0m'
    PS1='$( status=$?; [ $status -ne 0 ] &amp;&amp; echo "[$status] ")\['"$blue""$bold"'\]$\['"$reset"'\] '
    
    if [[ $EUID -eq 0 ]]; then
      PS1='$( status=$?; [ $status -ne 0 ] &amp;&amp; echo "[$status] ")\['"$red""$bold"'\]#\['"$reset"'\] '
    fi
    

    .inputrc:

    # vi mode, change to 'emacs' here if you prefer
    set editing-mode vi
    
    # vi INSERT prompt
    set vi-ins-mode-string "\1\e[30;44m\2 INS \1\e[0m\2 "
    
    # vi NORMAL prompt
    set vi-cmd-mode-string "\1\e[30;47m\2 NOR \1\e[0m\2 "
    
  • [Solved] Window manager with no (or very small) minimum window size?

    I need extremely small tiling windows, say something with only a width/height of 10px. However, by experimentation I found that my window manager Sway (and likely also i3) has a hard-coded minimum height of 60px and minimum width of 100px.

    So, dear Linux gurus, do you know of a window manager with configurable minimum tiling window size? Or with no minimum at all? Bonus point for running natively on Wayland.

    Edit: Found the culprit in the sway codebase:

    sway/include/sway/tree/node.h

    #define MIN_SANE_W 100 #define MIN_SANE_H 60

    In short, I am not sane.

    These constants are not used much in the codebase, so it shouldn't hurt to change it and compile from source. I will report results. Btw I opened an issue to see if they can be made configurable. Plus, some code archaeology suggests that this is not an issue in i3.

    Thanks for your suggestions. Some, like Hyprland and dwl, sound promising, but I'll try to make it work with Sway first.

    Why do I need this, you ask? It's a bit of a secret, but I've been working for about two years on a custom "operating system", or rather a suite of productivity tools unlike anything seen before. I'm about to finish it, but one of my last requirements to make it all click is a tiling window manager that is both extremely minimal and extremely customizable. It will eventually be released as free software for the benefit, amusement, and horror of everyone.

    Also the top 20px of my screen has burn-in and I want to declare it unusable at the window manager level. You see, I use Linux not only to flex, but also to live frugally.

    Edit 2: Compiling from source worked. Patch here:

    ``` diff --unified --recursive --text sway-git/include/sway/tree/node.h sway-git.new/include/sway/tree/node.h --- sway-git/include/sway/tree/node.h 2023-10-23 19:21:15.915536904 +0200 +++ sway-git.new/include/sway/tree/node.h 2023-10-23 19:30:18.638894754 +0200 @@ -4,8 +4,8 @@ #include #include "list.h"

    -#define MIN_SANE_W 100 -#define MIN_SANE_H 60 +#define MIN_SANE_W 20 +#define MIN_SANE_H 20

    struct sway_root; struct sway_output; ```

    15
    ColorCat - Render Hex Colors in your Terminal like an IDE

    Source code

    ``` #!/usr/bin/env bash

    colorcat

    - cats a file, but if any line contains N hex colors, it appends the colors

    (rendered as ansi escape sequences) to the end of the line.

    - input can be stdin, a file, or a hex color in plain text

    if [[ "$#" -eq 1 &amp;&amp; ! -f "$1" ]]; then echo "$1" else cat "$@" fi | while read -r line; do colors="" for word in $line; do if [[ "$word" =~ [A-Fa-f0-9]#?([A-Fa-f0-9]{6})[^A-Fa-f0-9]$ ]]; then hex=${BASH_REMATCH[1]} r=$((16#${hex:0:2})) g=$((16#${hex:2:2})) b=$((16#${hex:4:2})) truecolor="\033[48;2;${r};${g};${b}m" reset="\033[0m" colors="${colors}${truecolor} ${reset} " fi done echo -e "$line $colors" done ```

    3
    Cross-app URL aliases (like shell aliases) powered by Nginx.

    Source code:

    ```

    source of truth for URL aliases

    - used by url-alias-setup and url-alias

    - can be modified to add new aliases

    declare -A __url_alias=( ["g"]="https://google.com" ["r"]="https://reddit.com" ["h"]="https://news.ycombinator.com" )

    url-alias

    - print the current URL aliases

    function url-alias () { local green="\033[32m" local cyan="\033[36m" local reset="\033[0m"

    echo "${green}URL aliases:${reset}" for alias in "${!__url_alias[@]}"; do echo "${cyan}${alias}${reset} -> ${__url_alias[${alias}]}" done

    echo "${green}To add new aliases, edit the ${cyan}__url_alias${green} array and run ${cyan}url-alias-setup${reset}" }

    return either 'linux' or 'macos'

    function get_platform () { case "$(uname -s)" in Darwin) echo "macos" ;; Linux) echo "linux" ;; *) echo "unsupported platform" exit 1 ;; esac } platform=$(get_platform)

    url-alias-setup

    - sets up URL aliases

    - this is done by modifying the /etc/hosts file and the nginx configuration

    - if changes are made, nginx is (re)started

    function url-alias-setup () {

    nginx config (platform dependent)

    if [[ "$platform" == "macos" ]]; then local nginx_config="/usr/local/etc/nginx/nginx.conf" else local nginx_config="/etc/nginx/nginx.conf" fi

    create new nginx config and hosts file

    local new_hosts="" read -r -d '' new_nginx_config &lt;&lt; 'EOF' worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; EOF

    for alias in "${!__url_alias[@]}"; do local url="${__url_alias[$alias]}"

    new_hosts="${new_hosts}\n127.0.0.1 ${alias}"

    read -r -d '' server_blocks &lt;&lt; EOF server { listen 80; server_name ${alias}; location / { rewrite ^ ${url} permanent; } } EOF new_nginx_config="${new_nginx_config}\n ${server_blocks}" done new_nginx_config="${new_nginx_config}\n}"

    replace files

    if file already exists, prompt user to overwrite

    echo "Saving new nginx config and hosts file..." if [[ -f "${nginx_config}" ]]; then echo "File ${nginx_config} already exists. Overwrite? (y/n)" read -r overwrite if [[ "${overwrite}" != "y" ]]; then echo "Aborting..." return 1 fi fi echo -e "${new_nginx_config}" | sudo tee "${nginx_config}" > /dev/null if [[ -f "/etc/hosts" ]]; then echo "File /etc/hosts already exists. Overwrite? (y/n)" read -r overwrite if [[ "${overwrite}" != "y" ]]; then echo "Aborting..." return 1 fi fi echo -e "${new_hosts}" | sudo tee /etc/hosts > /dev/null

    start or restart nginx

    echo "Restarting nginx..." if [[ "$platform" == "macos" ]]; then nginx -s reload else sudo systemctl restart nginx fi } ```

    1
    My Worst Tech Interview Experience
    sebastiancarlos.com My Worst Tech Interview Experience

    My worst tech interview was with me as the interviewer. HR told me the applicant was a coworker’s friend with 10+ years of experience, so I…

    My Worst Tech Interview Experience
    8
    InitialsDiceBearhttps://github.com/dicebear/dicebearhttps://creativecommons.org/publicdomain/zero/1.0/„Initials” (https://github.com/dicebear/dicebear) by „DiceBear”, licensed under „CC0 1.0” (https://creativecommons.org/publicdomain/zero/1.0/)SE
    sebastiancarlos @lemmy.sdf.org
    Posts 50
    Comments 49