0

I want to quote the current directory in my prompt.

Eg, if I do:

mkdir $'new\nline'; cd $'new\nline'

I want my prompt to display $'new\nline', and NOT print a literal newline.

I'm seeing interesting behaviour trying to print backslashes (\) in bash 5.0.9:

p='\\n'       && echo -E "${p@P}"  # 2 slashes; output = '\n'
p='\\\\n'     && echo -E "${p@P}"  # 4 slashes; output = '\n'
p='\\\\\\n'   && echo -E "${p@P}"  # 6 slashes; output = '\\n'
p='\\\\\\\\n' && echo -E "${p@P}"  # 8 slashes; output = '\\n'

Note: ${parameter@P} is a string that is the result of expanding the value of parameter as if it were a prompt

Why is the output equivalent with (2 and 4), and (6 and 8) slashes?

Given this confusion, and:

mkdir '\\n' && cd '\\n'

I couldn't work out how to programatically transform \\n into a string such that it was displayed in a prompt either as: \\\\n or $'\\\\n', as well as handling the literal newline case.


How do I get directory names quoted in the prompt such that:

  • ~ is displayed for $HOME and a leading ~/ for subdirectories
  • Other paths are escaped only if required
  • A copy-paste of the displayed string is a valid shell token referring to the current directory

Eg "$HOME/dir with spaces" should be displayed as either:

  • ~/dir\ with\ spaces
  • ~/$'dir with spaces'
  • ~/'dir with spaces'

2 Answers 2

2

If I include \w in the prompt, and cd $'/tmp/new\nline', Bash shows just /tmp/newline in the prompt. It doesn't seem to print the literal newline, but that's also not an unambiguous output format.

${var@P} is meant to expand prompt-style escapes, like \u for username, \h for hostname and \w for the working directory, I doubt you want those here. Instead ${var@Q}, which quotes the output might be more useful?

Setting PS1='${PWD@Q}\$ ', I get the prompt: $'/tmp/new\nline'$, or '/tmp'$ if the path is nicer. An alternative might be PS1='$(printf "%q" "$PWD")\$ ', which gives a different quoting in some cases, and e.g. leaves the quotes out in the case of a "nice" path, so /tmp$.


To get the home directory shown as a tilde, one option would be to do that replacement manually:

set_ppath() {
    printf -v ppath "%q" "$PWD"
    ppath="${ppath/$HOME/"~"}";
}
PROMPT_COMMAND=set_ppath
PS1='$ppath\$ '

It still quotes the whole path if a part of it needs quoting, though. To work around that, I suppose you'd have to walk the path piece by piece.


I have no idea why ${var@P} collapses the backslashes like that, though.

1
  • Cheers! I wanted to add coloured / in my prompt's $PWD, so I came up with this.
    – Tom Hale
    Sep 25, 2019 at 10:09
0

Based on ilkkachu's answer, I came up with:

PROMPT_COMMAND=_prompt_bash_set

_prompt_bash_set() {
  # Working directory
  local prefix=''  # Used to contain '~' if $PWD directory is under $HOME
  local dir=$PWD

  # Replace leading $HOME with ~ if at beginning of string
  case "$PWD"/ in # Extra slash at end prevents substring match
    "$HOME"/)  # $PWD == $HOME itself
      prefix=\~  # no trailing /
      dir='' ;;
    "$HOME"/?*)  # $PWD is a subdir of $HOME
      prefix=\~/  # Add trailing / so that possibly quoted string goes after ~/ for path validity
      dir=${PWD#$HOME/} ;;   # Remove $HOME and / as this is now in $prefix
  esac

  if [[ $PWD != $HOME ]]; then  # $PWD != $HOME
    # Avoid ${PWD@Q} as it produced a literal \n inside $''
    printf -v dir '%q' "$dir"  # Quote directory minus $HOME

    # Replace a single slash with 4!?! slashes
    # https://unix.stackexchange.com/q/543473/143394
    dir=${dir//\\/\\\\\\\\}

    # Prevent command and variable substitution:
    dir=${dir//\`/\\\`}  # escape ` as \`
    dir=${dir//\$/\\\$}  # escape $ as \$
  fi

  PS1='\u@\h:'$dir'\$ '
}

This has the quoted directory as literal text in $PS1 rather than inclusion via a variable.

The advantage of doing it this way is that I can then go on to colour slashes in the directory which my eyes find easier to parse.

2
  • I'm pretty sure printf %q isn't POSIX either, but it's more widely supported than Bash's ${var@Q}. On the other hand, PROMPT_COMMAND might be Bash-only too.
    – ilkkachu
    Sep 25, 2019 at 10:41
  • @ilkkachu Thanks, I've updated the comment accordingly :)
    – Tom Hale
    Sep 25, 2019 at 12:15

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.