0

Suppose this is the output of pwd:

/home/amarakon/.local/src/amarakon/scripts/src

How can I change it to this:

~/.local/src/amarakon/scripts/src

I tried this command:

pwd | sed 's|/home/.*|~|'

But this it the output:

~
Amarakon
  • 333

4 Answers4

3

If using zsh, you can use:

print -P '%~'

To get the current working directory with your home directory replaced with ~. It will also replace /home/directory/of/otheruser with ~otheruser, if /home/directory/of/otheruser is otheruser's home directory and ~otheruser has been expanded before.

You'd normally put %~ in your $PROMPT to get the current working directory there. The -P option of print enables prompt expansion for that builtin.

Prompt expansion can also be done upon parameter expansion with the % parameter expansion flag:

printf '%s\n' ${(%):-%~}

That ~ replacement can also be enabled with the D parameter expansion flag, so you could also do:

printf '%s\n' ${(D)PWD}

You could also do it by hand with:

set -o extendedglob
printf '%s\n' ${PWD/#%(#b)$HOME(|\/*)/\~$match[1]}

Though beware that if $HOME has symlinks or . path components, or $PWD has symlink components, that may not work properly as you'd end up with the variables containing different representations of your home.

Doing it with sed would be cumbersome if wanting to do it for arbitrary values of $HOME. You'd need to escape / and regex operators in it first (. being a regex operator and common in user names), do it in the C locale as dir paths are not guaranteed to be text, load up all the lines of $PWD into the pattern space, do the substitution only at the start and only if followed by / or the end of the subject:

escaped_HOME=$(printf '%s\n' ~ | LC_ALL=C sed '
  :1
  $!{
    N
    b1
  }
  s:[][\\/.^$*]:\\&:g
  s/\n/\\n/g'
)
pwd | LC_ALL=C sed '
  :1
  $!{
    N
    b1
  }
  s/^'"$escaped_HOME"'\(\(\/.*\)\{0,1\}\)$/~\1/'

Not the best tool for that. With perl:

perl -MCwd -le 'print getcwd =~ s|^\Q$ENV{HOME}\E(?![^/])|~|r'

If $HOME is / as it often is for some system users, %~ will not replace / with ~ nor /dir with ~/dir which is probably just as well. The manual ones will replace / with ~, but not /dir with ~/dir which I'd consider acceptable.

2

As others have said, substituting whatever matches /home/.* with ~ will replace the whole string in the example you show since .* matches to the very end of the string.

The variable PWD will also hold the current directory's path, and we may use a much cheaper parameter substitution on that value:

[[ "$PWD/" == "${HOME%/}/"* ]] && printf '~%s\n' "${PWD#"${HOME%/}"}"

This tests whether $PWD contains $HOME at the start, and if so, prints $PWD with that part replaced by ~. This is much quicker as it does not require expensive (relatively slow to run) external utilities.

More portably:

case $PWD/ in ("${HOME%/}/"*) printf '~%s\n' "${PWD#"${HOME%/}"}"; esac
Kusalananda
  • 333,661
  • You'd get ~/ when HOME == PWD == /, but I guess that's acceptable. Not doing any substitution when HOME == / as zsh's %~ does is probably the best approach. – Stéphane Chazelas Jun 15 '22 at 07:29
0

. matches any character, so it'll match everything left on the line including the later slashes and path parts.

You basically want to limit that part of the pattern to a series of things that aren't slashes, so it stops the substitution when it encounters a slash (if it does):

pwd | sed 's|/home/[^/]*|~|'

You might consider requiring '/home' to be at the start in case you ever have subfolders named home outside of the root folder:

pwd | sed 's|^/home/[^/]*|~|'

That should work with the output of pwd, but if you're replacing $HOME with ~ in longer strings, you might keep it the first way.

frabjous
  • 8,691
-1

I know this is an old question but you can use string operators for that purpose:

$ pwd
/home/kali/Documents
$ [ -z "${PWD%%$HOME*}" ] && echo ${PWD/$HOME/'~'} || echo $PWD
~/Documents

$ pwd
/etc/systemd/network
$ [ -z "${PWD%%$HOME*}" ] && echo ${PWD/$HOME/'~'} || echo $PWD
/etc/systemd/network

$ pwd
/tmp/home/kali/Documents
$ [ -z "${PWD%%$HOME*}" ] && echo ${PWD/$HOME/'~'} || echo $PWD
/tmp/home/kali/Documents

Now, you specifically asked about sed, I don't think it's the best tool or even necessary but you'd do:

$ pwd | sed "s|^$HOME|~|"
~/Documents
  • Your approach with ${PWD%%$HOME*} would give the wrong result for users whose username is the prefix of another user's username (e.g. for vic if there was any other user who's username started with vic). – Kusalananda Mar 26 '24 at 07:53