2

I'd like to customize my bash shell in a very specific way, so specific that I don't know if it is possible. Currently, my shell looks like this:

myname@ubuntu /home/myname:
>>

Where myname is my username. I customized the shell with the following line in ~/.bashrc:

PS1='${debian_chroot:+($debian_chroot)}\u@\h `pwd`:\n>> '

When I hit enter, this is what happens:

myname@ubuntu /home/myname:
>>
myname@ubuntu /home/myname:
>>

Instead of this, I'd like the following to happen:

myname@ubuntu /home/myname:
>>
>>

Moreover, if I type a command, what happens should be this:

myname@ubuntu /home/myname:
>> echo hello
hello
myname@ubuntu /home/myname:
>>

The following should not happen

myname@ubuntu /home/myname:
>>echo hello
hello
>>

Is this possible? If so, how to do that?

UPDATE

I was able to achieve my goal thanks to the ChrisAga's response.

Here is the script

# don't put duplicate lines or lines starting with space in the history.
HISTCONTROL=ignoreboth

customprompt() {
    # the current number of lines in bash history:
    bash_history_size=$(fc -l -1)
    bash_history_size=${bash_history_size%%[^0-9]*}

    # set an initial value to the number of lines
    # in bash history stored from the last time
    # this function was executed. This avoids bugs
    # when running the first command in the current
    # shell session
    if [ -n "$bash_history_lastsize" ]; then
        bash_history_lastsize=0
    fi

    # if the current number of lines in bash history
    # is different from the last number of lines, then
    # we print the user name and the current directory.
    # otherwise, we just print >>
    if [ "${bash_history_size}" != "${bash_history_lastsize}" ]; then
        PS1='\[\033[01;32m\]\u@\h \[\033[00m\]`pwd`:\n>> '
    else
        PS1=">> "
    fi

    # update the last value to the current value
    bash_history_lastsize=${bash_history_size}
}

PROMPT_COMMAND=customprompt

2 Answers 2

3

Actually, there is a solution in plain bash !

The only limitation is incompatibility with duplicate prevention in bash command history. So if you don't mind having duplicates in your bash history you can set the following in your ~/.bashrc :

HISTCONTROL=ignorespace

function pprompt {
   local hcount=$(fc -l -1)
   echo ${hcount}
   hcount=${hcount%%[^0-9]*}
   if [ "${hcount}" != "${ocount}" ]; then 
      PS1="\[\e]0;\u@\h: \w\a\]\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w:\[\033[00m\]\n>>"
   else 
      PS1=">>"
   fi
   ocount=${hcount}
}

PROMPT_COMMAND=pprompt

By default HISTCONTROL=ignoreboth which is equivalent to ignorespace:ignoredups so you have to change it.

The pprompt function gets the last command in the history and compare its number to the previously stored value. If you just hit enter the number doesn't change, so if this number have changed, we set PS1 to the full prompt, otherwise, we set it to >>.

Finally PROMPT_COMMAND=pprompt thells bash to execute pprompt before echoing the primary prompt ($PS1).

NB1. If you don't like to display your home path as ~, you can replace \w by `pwd`.

NB2. If we could get the actual bash command number (the one we can display using !# in the prompt) instead of the history command number, we would get a solution compatible with history deduplication.

4
  • Thank you very much ! I think this is exactly what I want! Although the idea of creating my own shell sounds interesting, it has its limitations. But what you proposed sounds fantastic! Jun 9, 2020 at 19:44
  • Thanks @StudentofScience. It would be better if pprompt won't be based on bash history witch makes it incompatible with history deduplication. I did some researches and it appears that bash command number can only be used in the prompt (\#). A way to get this number would make this solution really fantastic ;-)
    – ChrisAga
    Jun 10, 2020 at 10:19
  • actually, I found a way to avoid history duplication. Just add HISTONTROL=ignoreboth to .bashr. That will ignore duplicated commands and also ignore commands that start with whitespace Jun 10, 2020 at 12:45
  • Yes, ignoreboth usually is the default, and if you set it, my solution doesn't works each time you issue the same command twice (or more).
    – ChrisAga
    Jun 10, 2020 at 18:37
1

As far as I know , you cannot do it simply with plain bash . But instead you have to implement your own shell almost from scratch ( Don't be afraid , I've done it and it requires less than 30 lines of code).

Here's the code (custom_shell.sh) :

RED='\033[0;31m'                         #Definition of some ASCII colors
WHITE='\033[1;37m'                       #Replace them with whatever color you want

TMP_COMM_BUF="/tmp/custom_shell_buf"     #For temporary storage of commands

if [ "$1" == "e" ]
then 
    printf "${RED}>> ${WHITE}"           #When you just pressed enter
else
    printf  "${RED}$PWD >> ${WHITE} "    #After the execution of some command
fi


exec 3<&1                                #Custom file descriptor
read -u 3 comm_buf                       #Read commands from stdin
echo $comm_buf  > $TMP_COMM_BUF          #Store commands (exec cannot execute multiple commands at once , so we need it)
chmod +x $TMP_COMM_BUF                   #Make it executable
if ! [ -z "$comm_buf" ] 
then
    echo custom_shell.sh >> $TMP_COMM_BUF      #Recover the prompt after the execution ( It's assumed that the name of the file is custom_shell.sh and it's available in the $PATH )
    exec $TMP_COMM_BUF                         #Execute !!!
else
    exec custom_shell.sh e                     #If nothing specified , then just show ">>"
fi

Limitations :

Since it cannot use line editing feature which is present in bash , you cannot use Ctrl+C for interruption or Ctrl+L to clear the terminal.And there's no command completion and no shell history.

Security considerations :

Since it uses an unencrypted file (/tmp/custom_shell_buf) to store commands before executing it , if someone just tampers it right before the phase of execution (between the lines 16 and 21) , it can cause execution of undesired commands.

So after all , it's enough as a hobby work and it also meets your needs.And you can add more features to it if you want.

If anyone has a better suggestion , I'm glad to hear.

Happy hacking ;)

3
  • thank you :) ! Do you where I can find more information to create a custom shell that has editing features (ctrl + c, ctrl + z, ctrl + l, command completion)? What about making it secure? Jun 2, 2020 at 12:42
  • @StudentofScience I don't think it's possible to do it easily with a shell script. But there are nice libraries like Editline which enables you to do some command completions like what bash does. However I encourage you to study the source code of GNU Bash if you have time to figure out how does it get the job done.( e.g for Ctrl+Z ) Jun 2, 2020 at 15:39
  • @StudentofScience And the problem with the security of the custom shell is that the "exec" command can only replace the image of the current bash process with only one process . So I needed to store multiple commands in a file to be able to execute it. But a safer option would be to store it in a variable instead of a temporary file. Jun 2, 2020 at 15:39

You must log in to answer this question.

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