29

I'm new to bash and would like my prompt to show something that in tcsh was trivial, yet after a good google search I still cannot do.

I would like my prompt to include only the current and parent directories like this:

/parent/currentdir $

In tcsh this is achieved by:

set prompt = "%C2 %"

However in bash so far I have only found that I have to parse pwd to obtain the same output.

Isn't there a simpler way, like doing:

export PS1="$(some_command) $" 
3
  • 5
    In theory, PS1='\w \$'; PROMPT_DIRTRIM=2 should give you the bash equivalent, but that doesn't work properly on my system.
    – Mikel
    Jul 18, 2015 at 23:15
  • It doesn't work at all on mine: GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin14).
    – twalbaum
    Aug 7, 2015 at 23:19
  • 2
    PROMPT_DIRTRIM was introduced in Bash 4. I just tested on Ubuntu 16.04 under WSL and it worked great! Dec 8, 2017 at 18:08

7 Answers 7

32

Bash's prompt control features are rather static. If you want more control, you can include variables in your prompt; make sure you haven't turned off the promptvars option.

PS1='${PWD#"${PWD%/*/*}/"} \$ '

Note the single quotes: the variable expansions must happen at the time the prompt is displayed, not at the time the PS1 variable is defined.

If you want more control over what is displayed, you can use command substitutions. For example, the snippet above loses the ~ abbreviation for the home directory.

PS1='$(case $PWD in
        $HOME) HPWD="~";;
        $HOME/*/*) HPWD="${PWD#"${PWD%/*/*}/"}";;
        $HOME/*) HPWD="~/${PWD##*/}";;
        /*/*/*) HPWD="${PWD#"${PWD%/*/*}/"}";;
        *) HPWD="$PWD";;
      esac; printf %s "$HPWD") \$ '

This code is rather cumbersome, so instead of sticking it into the PS1 variable, you can use the PROMPT_COMMAND variable to run code to set HPWD and then use that in your prompt.

PROMPT_COMMAND='case $PWD in
        $HOME) HPWD="~";;
        $HOME/*/*) HPWD="${PWD#"${PWD%/*/*}/"}";;
        $HOME/*) HPWD="~/${PWD##*/}";;
        /*/*/*) HPWD="${PWD#"${PWD%/*/*}/"}";;
        *) HPWD="$PWD";;
      esac'
PS1='$HPWD \$'

Since the shortened prompt only changed on a directory change, you don't need to recalculate it each time a prompt is displayed. Bash doesn't provide a hook that runs on a current directory change, but you can simulate it by overriding cd and its cousins.

cd () { builtin cd "$@" && chpwd; }
pushd () { builtin pushd "$@" && chpwd; }
popd () { builtin popd "$@" && chpwd; }
chpwd () {
  case $PWD in
    $HOME) HPWD="~";;
    $HOME/*/*) HPWD="${PWD#"${PWD%/*/*}/"}";;
    $HOME/*) HPWD="~/${PWD##*/}";;
    /*/*/*) HPWD="${PWD#"${PWD%/*/*}/"}";;
    *) HPWD="$PWD";;
  esac
}
PS1='$HPWD \$'

Note that you don't need to, and should not, export PS1, since it's a shell setting, not an environment variable. A bash PS1 setting wouldn't be understood by other shells.

P.S. If you want a nice interactive shell experience, switch to zsh, where all of these (prompt % expansions largely encompassing tcsh's, chpwd, etc.) are native features.

PS1='%2~ %# '
2
5

Setting PROMPT_DIRTRIM=2 should be all you need.

1
4

The syntax for obtaining the parent and current directories is taken from Munai's answer.

However, as noted by Gilles, that code only shows the current directory at the time .bashrc is loaded, but it won't change as you navigate the system to other folders.

Having this in your .bashrc file makes the prompt automatically updated to your current directory:

prompt_command () {
    PS1='$(basename $(dirname "$PWD"))/$(basename "$PWD") $ '
}
PROMPT_COMMAND=prompt_command
6
  • 1
    Is defining prompt_command() supposed to do something, on its own? It doesn't for me. Did you mean to also use prompt_command() in redefinitions of cd & co, like Gilles did? Or did you mean PROMPT_COMMAND='...'?
    – LarsH
    Jun 21, 2016 at 12:27
  • 2
    Also, this command fails when $PWD contains a space.
    – LarsH
    Jun 21, 2016 at 12:37
  • 1
    Thank you for bringing me back to this. Quoted the $PWDs to allow for spaces and included the final command. My current prompt is more complex than this, therefore the need for the prompt_command definition. Otherwise, PS1=... works on its on.
    – twalbaum
    Jul 8, 2016 at 20:36
  • Any ideas for cleaning up the output when navigating to /? As written, you get /// $
    – ezrock
    Nov 10, 2017 at 18:14
  • Has anyone been able to develop a similar solution for Windows Command Prompt and/or PowerShell? When I use for %I in (.) do prompt %~nxI$G, the current folder is displayed, but it is not dynamic. So, it won't change when I change folders. Reference: Get current folder name by a DOS command? Jun 5, 2021 at 15:53
3

Prompt string can be easily changed in bash by editing the shell variable PS1. It stands for Prompt String 1. More info here.

For now fire up your bash shell.

vi ~/.bashrc

Append the PS1 definition in the file

`export PS1="$(basename $(dirname $PWD))/$(basename $PWD)"`

More tutorials here and here, to help you tweak it even more.

9
  • 1
    I understand how to modify the prompt. My question is how to get only the current and parent directories, not the whole path.
    – twalbaum
    Jul 18, 2015 at 20:50
  • @twalbaum edited my answer Jul 18, 2015 at 20:59
  • glad to help @twalbaum :) Jul 18, 2015 at 21:04
  • 1
    This sets the prompt to the current directory at the time .bashrc is loaded, the prompt won't change if you cd into another directory! @twalbaum Jul 19, 2015 at 21:16
  • @Gilles it does for me.
    – twalbaum
    Jul 19, 2015 at 21:19
2

Adding export PROMPT_DIRTRIM=2 to your bash file will do it for recent versions of bash (v. 4+). Specifically, this alters the appearance of the \w entry in the PS1 environment variable from full path to last two entries.

Final results looks like ../Documents/MyFolder

0

May be simpler one with "~" for Home directory.

function PWDN {
  echo "${PWD/#$HOME/~}" | rev | cut -d "/" -f1 -f2 | rev
}
0
export PS1='[\u@\h $(basename $(dirname ${PWD}))/$(basename ${PWD})]\$ '
export PROMPT_COMMAND='echo -ne "\033]0;$(basename $(dirname $PWD))/$(basename ${PWD})\007"'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.