3

I would like my bash prompt to show \etc\defaults using PS1 when I am in /etc/defaults.

I have tried these two methods

PS1="$(echo "\w" | sed 's/\//\\/g')"
PS1="$(tmp="\w" && echo ${tmp//\//\\})"

but both of them result in /etc/defaults.

I find it odd since both

echo $PWD | sed 's/\//\\/g'
tmp=$PWD && echo ${tmp//\//\\}

print \etc\defaults.

Noob Life
  • 151
  • 3
  • 2
    PS1="$(echo "\w" | sed 's/\//\\/g')" with the double quotes tells the shell to run that echo+sed pipeline right there, and to store the result in PS1. It'll just do the same as PS1='\w'. You need to escape the $ or put the thing in single quotes to tell the shell to store that command substitution in PS1. Though you'll get into a bit of an escape hell there. – ilkkachu May 03 '22 at 07:43
  • Not sure why you'd want to show the path as \etc\defaults though; you can't use it in that format as input to anything, and everything else will show it with the slashes that actually are there. All you'll get is one thing confusingly showing a different thing from all the others. – ilkkachu May 03 '22 at 07:46
  • 1
    Why? You are doing yourself a great dis-service by refusing to learn that the unix path separator is /, not . Unix is not MS Windows, and deceiving yourself that it is will eventually and inevitably result in you making some disastrous mistake when typing in a command. – cas May 03 '22 at 07:46
  • Are you using WSL? – terdon May 03 '22 at 10:02
  • @cas I am going through a book called "Linux Basics For Hackers", and this was one of the questions/challenges. I don't plan to use it for anything, but I learned a lot from attempting. – Noob Life May 03 '22 at 10:53
  • @ilkkachu Thank you, your comment was really helpful for me to understand why my method did not work. – Noob Life May 03 '22 at 10:54

2 Answers2

4

Regardless of what we think about replacing the slashes with backslashes, this is actually somewhat interesting as a quoting hell issue. One that would come up with other similar constructs.

If your PS1 contains $(echo "\w" | sed 's/\//\\/g'), what happens is that first Bash processes it for the prompt escapes, like \w. So echo "\w" turns into echo "/etc/defaults", which basically works for nice pathnames. But at the same time, it also changes the \\ to a single \... So what you need to get in PS1, is $(echo "\w" | sed -e 's/\\//\\\\/g'). Or with double-quotes, the same with more backslashes: $(echo "\w" | sed -e "s/\\\\//\\\\\\\\/g").

Then there's the issue of the assignment to PS1, and there, you're probably going to want a single quoted string, which has to contain single quotes, so you need to replace them with '\''. It becomes something like this:

PS1='$(echo "\w" | sed -e '\''s/\\//\\\\/g'\'')'

Which is awkward enough that the SE syntax highlighter doesn't seem to follow it correctly, it shows the part between the '\''s as unquoted.

But... there are better ways of doing that.


The \w replacement is a bit suspect, since straight text replacement of data on the shell command line is basically an invitation for trouble. If the path was /tmp/quote"me, a straight replacement would produce echo "/tmp/quote"me", with a stray quote. Bash actually does add backslashes to escape the quote, so it becomes echo "/tmp/quote\"me", which is ok. But it's still icky, so I'd rather avoid it. There's a very plausible command injection issue looming there if ever the escaping doesn't match the parsing. E.g. the similar echo '\w' would not work, it'd cause extra backslashes to show up if the path contained any of "$\, and would completely blow up and be open for various vulnerabilities if it contained a '. (Similar issues as with SQL injections.)

Also, we can get rid of the quoting hell by defining a function to run that sed, hiding the s/// command from the prompt itself. Incidentally, we have to use $PWD here, as the function itself isn't part of the prompt, so \w won't work:

manglepath() {
    echo "$PWD" | sed 's/\//\\/g'
}
PS1='$(manglepath)\$ '

And actually we should be able to use the ${var//pattern/replacement} expansion instead of calling sed, and we probably should use printf instead of echo:

manglepath() {
    printf "%s\n" "${PWD//\//\\}"
}
PS1='$(manglepath)\$ '
ilkkachu
  • 138,973
2
PS1='$(echo "\w" | sed '\''s#/#\\\\#g'\'')'

This will result in PS1 to be $(echo "\w" | sed 's#/#\\\\#g')

Noob Life
  • 151
  • 3