43

pwd gives me

/data/users/me/some/random/folder

Is there an easy way of obtaining

~/some/random/folder

from pwd?

Volker Siegel
  • 16,983
  • 5
  • 52
  • 79
Sibbs Gambling
  • 1,646
  • 6
  • 20
  • 26

6 Answers6

42

If your're using bash, then the dirs builtin has the desired behavior:

dirs +0
~/some/random/folder

(Note +0, not -0.)

With zsh:

dirs
~/some/random/folder

To be exactly, we first need to clear the directory stack, else dirs would print all contents:

dirs -c; dirs

Or with zsh's print builtin:

print -rD $PWD

or

print -P %~

(that one turns prompt expansion on. %~ in $PS1 expands to the current directory with $HOME replaced with ~ but also handles other named directories like the home directory of other users or named directories that you define yourself).

Stephen Kitt
  • 411,918
  • 54
  • 1,065
  • 1,164
chaos
  • 47,463
  • 11
  • 118
  • 144
  • I switch all the time between bash and zsh. Unfortunately this solution forces me to remember the difference between the shells and also to remember (to check) where I am currently. The answer by @Bichoy is shell-agnostic for these two. – avp Sep 28 '18 at 13:39
27

You can use Bash variable substring replacement feature:

$ pwd
/home/vagrant/foo/bar
$ echo ${PWD/#$HOME/'~'}
~/foo/bar
Nykakin
  • 3,919
  • 1
  • 19
  • 18
  • 2
    This also works in `ksh` and `zsh`. – mr.spuratic Jun 03 '15 at 08:59
  • 1
    While I have upvoted this answer because its the easiest way, there's a small caveat here: inside /tmp/home/username, this will return /tmp~. I cannot find a way to insert a caret sign before $HOME to fix this. If you need your solution to be perfect, don't use this... – 0fnt Jun 03 '15 at 09:47
  • 1
    @user18151, than you for pointing this out. I replaced my answer to `${string/#substring/replacement}` version to prevent this behaviour. – Nykakin Jun 03 '15 at 09:54
  • 1
    @mr.spuratic it's only correct (except for the usage of `echo`) in `zsh`. In other shells, `$HOME` would be treated as a pattern and the whole thing subject to split+glob. – Stéphane Chazelas Jun 03 '15 at 11:07
  • 1
    Escaping the tilde ('~') did the trick. Note that \~ also works. This is convenient when already in '-context: e.g., `PROMPT_COMMAND='printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}"'` – Atafar Feb 08 '19 at 11:38
  • Very helpful when configuring .bashrc to display current dir & command in ubuntu title bar. – jerome Dec 02 '19 at 15:53
9

Using sed:

pwd | sed "s|^$HOME|~|"

The idea is to use any character that is less likely to appear in a home directory path as the delimiter in sed regex. In the example above I am using |.

The good thing about this technique is that it is actually shell-independent.

You can also alias pwd to /bin/pwd | sed "s|$HOME|~|" to get this behavior in other scripts that might be using pwd.

Bichoy
  • 3,056
  • 2
  • 19
  • 33
  • 2
    You need to include the `^` anchor in your regex. Otherwise, it is possible (however unlikely) for this to replace a segment in the middle of the path. – Sildoreth Jun 03 '15 at 13:33
  • Actually the regex should look as follows `"s|^/home/user\(/.*\)\?$|~\1|"`. Otherwise it matches other users' paths if their names are like `user1`, `user2`, `user3` and you match just `user` – avp Sep 28 '18 at 13:24
  • This is the only solution I've found that works with dash's PS1 prompt. Full solution: ```PS1='`printf "\e[32;1m"``whoami`@`hostname``printf "\e[37m"`:`printf "\e[34m"``pwd | sed "s|^$HOME|~|"``printf "\e[0m"`$ '``` – Jonathan Wheeler Dec 11 '20 at 21:22
3
tilde=\~${PWD#~}

I think that's what you want?

What's neat about the tilde is when it is expanded as a pattern it doesn't need quoting. Using $HOME in that same way without quotes would render unpredictable results because any of its constituent characters could be pattern characters, and so it might not always render the result expected.

But the tilde expansion is spec'd to always occur as if it were the result of a quoted expansion, and so its results are predictable. This seems to hold true in practically every shell I've tested for both case patterns and parameter patterns with the notable exceptions of both ksh93 and mksh for whatever reasons - neither of which seems to honor the quoted aspect of the tilde in that capacity.

Another neat thing about this is that you can redefine $HOME to handle any directory at all in the same fashion. For example:

cd ~
HOME=/some/prefix/I/would/like/to/trim 
#or, perhaps more usefully, some scripted means of arriving at same
tilde=\~${OLDPWD#~}
HOME=$PWD

Here's another example:

set 1 2 3 4 5 \~
cd ~; cd -; HOME= IFS=/
for d do shift
      HOME=${*#~/}/$d
      set ~ "$d" $HOME
done; cd -; HOME=$PWD
printf %s\\n "$@"
5/4/3/2/1/2/3/4/5/~/1/2/3/4/5/~
~
5
4
3
2
1
2
3
4
5
~
1
2
3
4
5
~
mikeserv
  • 57,448
  • 9
  • 113
  • 229
  • 1
    That's a really interesting approach. But it assumes that `$PWD` is under `$HOME`. You could use the same idea combined with pattern substitution to make it more general. How's `${PWD/#~/\~}`? – Mikel Jun 03 '15 at 16:52
  • 1
    Actually Nykakin's answer is basically that. – Mikel Jun 03 '15 at 16:53
0

You can try replacing the unwanted part with sed:

pwd | sed 's,^/data/users/me,~,'

or even better:

pwd | sed "s,^$HOME,~,"
Sildoreth
  • 1,822
  • 8
  • 23
  • 40
user1403360
  • 2,000
  • 1
  • 12
  • 11
  • 1
    You need to use the `^` anchor to ensure that only matches at the beginning of the directory path are replaced. Also, you don't need to use `g` because the command should be replacing 1 occurrence at most. – Sildoreth Jun 03 '15 at 13:41
  • Thank you. I am still learning this things and you comment is really helpfull. – user1403360 Jun 04 '15 at 06:54
  • Problematic if my home directory is `/home/alice` and my current directory is `/home/aliceinchains`. But if the only time I ever want to use the feature is when I know I'm my home directory or one of its subdirectories, then it should work. – Mihai Danila Jul 22 '17 at 21:53
  • @MihaiDanila How about `pwd | sed -e "s,^$HOME/,~/," -e "s,^$HOME,~,"` – SergiyKolesnikov Jun 24 '21 at 11:38
  • The second expression has a bug in that it still matches aliceinchains; you need to finish the match pattern with a dollar sign. Other than that, it should work (do the other dollar signs need escaping?). – Mihai Danila Jun 25 '21 at 13:45
0

With Zsh it's as simple as ${(D)PWD}.

See under "Parameter Expansion Flags" in zshexpn(1):

D

Assume the string or array elements contain directories and attempt to substitute the leading part of these by names. The remainder of the path (the whole of it if the leading part was not substituted) is then quoted so that the whole string can be used as a shell argument. This is the reverse of `~' substitution: see the section FILENAME EXPANSION below.

ericbn
  • 201
  • 2
  • 3