0

I want to create an interactive CLI in bash. Similar to the way bash prefixes every line with user@host:~$, I would like to prefix every line with foobar>. This functionality can be seen in CLI applications like Metasploit or mysqli, but they use separate programming languages whereas I would like to simply use bash scripting. Additionally, I need the up/down arrow keys to use the history functionality (or something similar), allowing the user to easily bring up the last input or the nth previous to that.

In trying to create a solution, I came across this answer which allows the arrow keys to be interpreted by bash and bring up the last input using history. This works well, however I am having a hard time combining this with line prefixing. I found a solution here to use coproc to preprocess stdout and implemented a version of this, however, this method neither works for the input line you are typing on, nor the lines pulled up using the arrow keys with history. The bash script I have so far is below:

exec 4>&1
coproc out (sed 's/^/foobar> /' >&4)
exec 1>&${out[1]}

printf "foobar> " >&4
while read -e x; do
   history -s "$x"
   echo $x
done

Here is the output which details both problems. As you can see below, I am able to prefix the very first line of the cli by using printf "foobar> " >&4 in my above script before the while loop. Below I type boop on this first line, and the output from echo is redirected to my coproc. However after this, using the up arrow to bring the input boop back will print boop without my prefix (as seen on the last line).

$ ./test.sh
foobar> boop
foobar> boop
boop

I do realize that because I don't have a printf inside the loop, the line I am typing on isn't going to be prefixed after the first iteration. I can fix this later by reworking the loop (though I am worried about sending the prefix line to the prefixing coproc, thus double prefixing). What I mainly need to fix is the up arrow output not getting prefixed. I was under the assumption this information was sent via stdout, but apparently it isn't. I've tried redirecting stderr in the same manner as well but that stops the terminal from echoing what I type as I type it.

I need a solution for prefixing each line in a bash script, whether it builds off the script I have provided above or uses a different method. I would also like it to be as elegant as possible, as I am going to be building a CLI tool using this technique and would prefer as little extra overhead as possible. I thought redirection would do this for me but apparently it's more complicated than I first thought.

2
  • 1
    The PS should be a separate question. Commented Dec 2, 2019 at 22:14
  • You're correct, it should. I will make one if it doesn't exist already Commented Dec 4, 2019 at 15:30

1 Answer 1

1

I'm not really following this coproc thing. In your case read -p seems just right. And you may want to use a separate (non-default) history file.

Warning: the script creates/uses $HOME/.custom_history to save its history. After you test the solution, remove the file by hand.

#!/bin/bash

# History management
hfile="$HOME/.custom_history"
HISTCONTROL=ignoreboth
history -r "$hfile"
trap 'history -a "$hfile"' exit

# Main loop
while read -ep 'foobar> ' x; do
   history -s -- "$x"
   printf '%s\n' "$x"
done

Basic idea borrowed from this question.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .