4

So I know that pwd and cd in the shell have a "logical" and "physical" mode (see this SO question and questions linked from there).

Now, assume I have this symlink:

/home/<username>/foo -> /tmp/bar

To follow along locally, just do this:

mkdir /tmp/bar
ln -s /tmp/bar ~/foo

Now, when I start in my $HOME and cd into foo, I get the "logical" path (with default settings) with pwd, and can get the "physical" path with -P:

$ cd ~/foo
$ pwd
/home/<username>/foo
$ pwd -P
/tmp/bar

Of course, ".." always refers to the parent of the "physical" path (my understanding is that it is not even interpreted by the shell, just passed to the command as-is, and some special built-in commands like cd interpret it specially to get the "logical" behavior):

$ pwd
/home/<username>/foo
$ realpath ..
/tmp

What I want is when I'm in $HOME/foo (or any of its subdirectories) that any relative paths I use (e.g. ls -l ../..) to be interpreted/expanded relative to the "logical" path (it's fine if I have to prefix my command with something).

For example:

$ pwd
/home/<username>/foo
$ vi ../something.txt

I want this to resolve to vi /home/<username>/something.txt.

Is there something easier/shorter than doing the following (or writing a shell function that I can prefix my command with that goes over all arguments and - if they refer to paths - expand them before passing them to the command)?

$ pwd
/home/<username>/foo
$ realpath -L $PWD/../something.txt
/home/<username>/something.txt
$ vi $(realpath -L $PWD/../something.txt)

Is there something like a "logical path expansion for command line parameters that are relative file paths being done before the shell executes the command"? I'm using zsh, but any solutions for bash or zsh would be fine.

1

I'll answer this for Zsh, since that's what I'm using.


To get the effect you want with .. when using cd, simply add the following to your ~/.zshrc file:

setopt NO_chase_dots NO_chase_links

Documentation here: http://zsh.sourceforge.net/Doc/Release/Options.html#index-CHASEDOTS


For all other cases, instead of .., you will need to use the :h ("head") expansion modifier:

$ pwd
/home/<username>/foo
$ vi $PWD:h/something.txt

If you need to reach up more than one level, you can append a number to it (provided you surround the expression with curly braces) or just repeat the :h:

# These two are equivalent:
$ vi ${PWD:h2}/foo
$ vi $PWD:h:h/foo

Documentation here: http://zsh.sourceforge.net/Doc/Release/Expansion.html#Modifiers

  • Thanks, the :h is something I can use :) I heard about modifiers in a zsh session with @guckes years ago, but totally forgot to look there. – Thomas Perl Nov 29 '20 at 9:33

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.