2

Let's say I am in console in a subfolder, that is a child (or grandchild, etc.) of a symlinked (soft) folder.

From the subfolder, if I do ls -la nothing indicates all the files are inside a symlinked folder.

How could I know that? Currently, I have to go to the parent folder to know that.

But it means finding by test error, testing all the hierarchy of folders until arriving to the parent (linked).

Ferroao
  • 303

3 Answers3

3

The fact you navigated to the current working directory by following some symbolic link(s) is only tracked by your shell, i.e. by Bash. The path that led you here is called the logical path, it can contain directory components that are in fact symbolic links to directories. The path after resolving all the symbolic links is called the physical path.

A tool that is neither your current Bash nor any of its builtins, when it gets spawned as a separate process in the current working directory of the shell, it knows the physical path to the directory naturally, because the physical path is the one the OS associates with the process (/proc/self/cwd for the process). The tool can know the logical path by examining the PWD variable in its environment or by using anything that examines the variable. The value of the variable comes from your Bash.

In Bash the builtins cd and pwd can operate on physical or logical paths, see this answer. pwd that is not a builtin (e.g. /usr/bin/pwd) can tell you the logical path only because it checks the PWD variable in its environment. In a symlinked directory compare these:

$ env pwd -L
/logical/path
$ env pwd -P
/physical/path
$ env --unset=PWD pwd -L
/physical/path

Note all these pwds are not builtins; env itself in not a shell builtin and it can only run pwd executables, not builtins. By removing PWD from the environment we made pwd -L behave like pwd -P.

ls does not care about PWD in the environment. The possible discrepancy between the physical and the logical path is irrelevant for what ls does. You may have thought it should be different. In the first version of your question you made an incorrect assumption that all files in a symlinked directory are symlinks; with this assumption it's easy to further assume ls hides this fact. The truth is ls in some (physical) directory works in the same way, no matter what logical path led your shell to the directory.

You can detect discrepancy between the physical and the logical path by comparing the output of pwd -P to the output of pwd -L (or directly to $PWD). Let's build a basic warning into ls:

function ls (
   p="$(pwd -P)"
   command ls "$@"
   [ "$p" != "$PWD" ] && >&2 printf 'ls: warning: ./ -> %s/\n' "$p"
)

I used a function, not an alias, because alias ls='ls --color=auto' is quite common and I don't want to break it; also because aliases are not for this. The highly probable existence of the alias makes the syntax function ls … way better than the portable ls () … (the latter triggers the alias).

This basic function is not perfect. ls /some/other/dir will never warn you about /some/other/dir, it will warn you about ./ (if symlinked) in spite of ./ being irrelevant in this case.

Anyway, the answer to "how could I know that I am in a subdirectory that is a (grand-…)child of a symlinked directory?" is:

[ "$(pwd -P)" != "$PWD" ]
1

I added $(pwd -P) to my bash prompt; this gives the real folder name instead of the symlinked one. There's no command switch with ls that I know of that will give you what you want. But at least changing the prompt will give you the exact location you're currently in.

ajgringo619
  • 3,276
1

Based on @ajgringo619

This solves it:

# includes complete left path
alias ll2='if [[ $(diff <(ls "$PWD" | sed "s|.*|$(ls -d "$PWD/")&|g") <(ls -d "$(pwd -P)/"*) | wc -l) == "0" ]]; then ls -l; else paste -d " -> " <(ls "$PWD" | sed "s|.*|$(ls -d "$PWD/")&|g") /dev/null /dev/null /dev/null <(ls -d "$(pwd -P)/"*); fi'

or

alias ls2='if [[ $(diff <(ls "$PWD" | sed "s|.|$(ls -d "$PWD/")&|g") <(ls -d "$(pwd -P)/") | wc -l) == "0" ]]; then ls -l; else paste -d " -> " <(ls "$PWD") /dev/null /dev/null /dev/null <(ls -d "$(pwd -P)/"*); fi'

Ferroao
  • 303