Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

get parent directory of a file in bash

Tags:

linux

bash

shell

sh

How do I get a parent directory for a file?

I want it to be safe on all kind of names:

.
..
path/to/my/file
/absolute/path/to/my/file
'-rf --no-preserve-root whatever'/test.zip
(symbolic links)
`'"`'{(})

I am more interested getting the canonical location on the file system than in traversing the path stated in the filename.

Note that there are similar questions to this one, but none of them focuses on correctness, relative/absolute paths and "unsafe" names:

[1] bash get the parent directory of current directory

[2] Retrieve parent directory of script

[3] bash filepath to parent directory of file

like image 369
VasiliNovikov Avatar asked Dec 23 '22 23:12

VasiliNovikov


2 Answers

Get parent directory of your current directory:

parent_dir="$(dirname -- "$(realpath -- "$PWD")")"

Get the directory of the script you're running:

parent_dir="$(dirname -- "$(realpath -- "$0")")"

Get parent directory of anything:

parent_dir="$(dirname -- "$(realpath -- "$file_or_dir_name")")"

If your system does not have realpath but does have readlink, this should work:

parent_dir="$(dirname -- "$(readlink -f -- "$file_name")")"

Enjoy!

like image 149
VasiliNovikov Avatar answered Jan 08 '23 09:01

VasiliNovikov


Bash's cd command has a couple of interesting but little-used options, -P and -L.

   cd [-L|[-P [-e]] [-@]] [dir]
      ...    The  -P  option  causes  cd to use the physical directory
      structure by resolving symbolic links while traversing  dir  and
      before processing instances of .. in dir (see also the -P option
      to the set builtin command); the -L option forces symbolic links
      to  be followed by resolving the link after processing instances
      of .. in dir. ...

So ... if you're looking for the physical location in the filesystem of your current working directory, you could use something like this:

realwd="$(cd -P .; pwd)"

In your comments, you mentioned that you're looking for the parent directory of the directory containing a file -- so, if a path is /foo/bar/baz/filename, you'd be looking for /foo/bar.

To get this, I would suggest a combination of cd -P and parameter expansion. Since you know that the / character can never exist as part of a filename, the following might work for you:

grandparent() {
    local realdir="$(cd -P "${1%/*}"; pwd)"
    echo "${realdir%/*}"
}

This works by using cd -P to "get" the physical location of the file, then parameter expansion to strip off the last item in the path.

$ mkdir -p one/two/three
$ touch one/two/three/foo
$ ln -s one/two/three bar
$ ls -l bar
lrwxr-xr-x  1 ghoti  wheel  13 Nov 19 23:05 bar -> one/two/three
$ grandparent bar/foo
/usr/home/ghoti/tmp6/one/two
like image 43
ghoti Avatar answered Jan 08 '23 08:01

ghoti