#!/usr/bin/env bash usage() { less -FEXR <<'HELP' fzrepl interactively edit stdin using stream filters like awk, sed, jq -c, --cmd command used to filter input -q, --query default command string to use command history can be saved to a file by setting the environment variable FZREPL_FILE examples: echo 'foo bar' | fzrepl -c 'awk {q}' -q '{print}' echo 'hello world' | fzrepl -q p 'sed -n {q}' FZREPL_FILE=jqhistory fzrepl jq package.json HELP } # TODO: better "wrapping", this is painful: # fzrepl 'node -e {q}' -q "done = data => data;\nlet A='';process.stdin.on('data',x=>A=A.concat(x.toString())).on('end',()=>{let d = done(A);process.stdout.write(`${String.prototype.trim.call(typeof d==='string'?d:JSON.stringify(d,null,2))}\n`)})" declare tmpfile=/tmp/fzreplinput declare cmd declare default_query declare output declare -A colors colors[red]=$(tput setaf 1) colors[green]=$(tput setaf 2) colors[reset]=$(tput sgr0) cleanup() { [[ -e "$tmpfile" ]] && rm "$tmpfile" } trap cleanup SIGHUP SIGINT SIGTERM color() { local color color="$1"; shift printf '%s' "${colors[$color]}" "$*" "${colors[reset]}" } err() { color red "$@" >&2 return 1 } die() { (( $# > 0 )) && err "$@" exit 1 } for arg; do case $arg in -q|--query) [[ -z $2 || $2 = -* ]] && die "missing argument to $1" default_query="$2" shift 2 ;; -c|--cmd) [[ -z $2 || $2 = -* ]] && die "missing argument to $1" cmd="$2" shift 2 ;; -h|--help) usage; exit ;; *) break 2; esac done if [[ -z $cmd && -n $1 && ! -f $1 ]]; then cmd="$1" shift fi if [[ -z $cmd ]]; then usage exit 1 fi if [[ $cmd != *'{q}'* ]]; then cmd+=' {q}' fi if [[ -n $1 && -f $1 ]]; then file=$1 shift fi if [[ -z $file ]]; then file=/dev/stdin fi mapfile -t REPLY < <( tee "$tmpfile" < "$file" | fzf \ --sync \ --ansi \ --height=100% \ --phony \ --print-query \ --query="$default_query" \ ${FZREPL_FILE:+--history=$FZREPL_FILE} \ --preview="$cmd < '$tmpfile'" ) q="${REPLY[0]}" q=${q@Q} echo "${cmd//'{q}'/$q}"