I would like to programmatically find the directory depth of a relative path. So for example the depth of test/dir/hello is 3

More specifically I would like the directory depth so that I can create a symlink to a file located to the parent directory.

I have the following 2 parameters: ${current_path} and ${parent_file_to_lunk}

How can I determine the directory depth in ${current_path} so that I can create a relative symlnk to ${parent_file_to_lunk}?

Something like this but with as many ../ as the directory depth:

cd ${current_path} ; ln -s ../$parent_file_to_link}
  • See also GNU ln's -r option. With bash -O extglob, see ${current_path//+([^\/])/..} – Stéphane Chazelas Mar 19 '15 at 13:26
  • Do you mean that you want to create a /home/kostas/test/dir/hello/link -> /home/kostas/file/to/link actually expressed as a relative link /home/kostas/test/dir/hello/link -> ../../../file/to/link? – Stéphane Chazelas Mar 20 '15 at 14:21

Try

parent_path=$(echo "$current_path"/ | sed -e "s|[^/]||g" -e "s|/|../|g")
cd "${current_path}" ; ln -s "${parent_path}${parent_file_to_link}"

This works simply by counting the slashes in "${current_path}".  The desired depth is one more than the number of slashes (e.g., the depth of test/dir/hello, which contains two slashes, is 3), so we simply add a slash: echo "$current_path"/.  Pipe it into sed.  Since we’re manipulating slashes, it’s easier to use a character other than / as the delimiter for sed’s s command; I like to use the vertical bar (|).  s|[^/]||g finds all characters that are not slash and replaces them with nothing.  In other words, it deletes all characters except for the slashes.  So, for the "${current_path}" value of test/dir/hello, we echo’d test/dir/hello/ and then chopped that down to ///.  Then s|/|../|g" changes every / into ../, so we end up with ../../../.

Note: this assumes that "${current_path}" does not have any excess (unnecessary) slashes in it.  For example, test/dir//hello and test/dir/hello/ are logically equivalent to test/dir/hello, but they contain a misleading number of slash characters, which will corrupt this process.

P.S. Always quote all shell variables unless you have a reason not to and you’re sure you know what you’re doing.  Using braces (as in ${variable_name}) is not the equivalent to quoting.

something like this should work

file="/var/log/dmesg"
ln -s $file $(dirname $file)/../

Assuming you want to create a:

/home/kostas/test/dir/hello/link -> /home/kostas/file/to/link

symlink (assuming /home/kostas is your current directory), but using a relative link, that is:

/home/kostas/test/dir/hello/link -> ../../../file/to/link

Then you could do (with GNU ln):

$ current_path=test/dir/hello
$ parent_file_to_lunk=file/to/link
$ ln -rsvt "$current_path/" "$parent_file_to_lunk"
‘test/dir/hello/link’ -> ‘../../../file/to/link’

Without GNU ln, but with GNU bash -O extglob or zsh -o kshglob or ksh93:

$ ln -s "${current_path//+([^\/])/..}/$parent_file_to_lunk" "$current_path/"

(note that it assumes that the components of $current_path are not symlinks themselves)

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.