In Bash, if VAR="/home/me/mydir/file.c", how do I get "/home/me/mydir"?
 
    
    - 1
- 16
- 47
- 69
 
    
    - 20,830
- 29
- 89
- 135
- 
                    A much more sophisticated and complex real directory path resolution is here https://stackoverflow.com/questions/29789204/bash-how-to-get-real-path-of-a-symlink/55254754#55254754 – Arunas Bartisius Jul 23 '21 at 11:34
9 Answers
dirname and basename are the tools you're looking for for extracting path components:
$ VAR='/home/pax/file.c'
$ DIR="$(dirname "${VAR}")" ; FILE="$(basename "${VAR}")"
$ echo "[${DIR}] [${FILE}]"
[/home/pax] [file.c]
They're not internal bash commands but they are part of the POSIX standard -  see dirname and basename. Hence, they're probably available on, or can be obtained for, most platforms that are capable of running bash.
 
    
    - 854,327
- 234
- 1,573
- 1,953
- 
                    4Why the use of brackets around the variable names, and not "$VAR" for example? – user658182 Jul 30 '17 at 12:34
- 
                    4https://stackoverflow.com/questions/8748831/when-do-we-need-curly-braces-around-shell-variables answers the above question. – user658182 Jul 30 '17 at 12:34
- 
                    1@user658182 In this particular example, it is done out of habit, not necessity. – Abandoned Cart Nov 14 '19 at 04:45
- 
                    The `export` is unnecessary and the [`echo`s are useless.](http://www.iki.fi/era/unix/award.html#echo) – tripleee Aug 28 '21 at 18:09
- 
                    2@tripleee: the `export` is a habit of mine, simply to ensure the variable is passed to sub-shells. The `echo` statements are to show how you could get the output into a variable, but I should probably have gone the whole hog on that (which I now have). Though neither of those really affect the "meat" of the answer, I'll adjust. I'm always appreciative of constructive criticism on improving my answers. – paxdiablo Aug 28 '21 at 23:59
$ export VAR=/home/me/mydir/file.c
$ export DIR=${VAR%/*}
$ echo "${DIR}"
/home/me/mydir
$ echo "${VAR##*/}"
file.c
To avoid dependency with basename and dirname
 
    
    - 300,895
- 165
- 679
- 742
 
    
    - 3,327
- 4
- 25
- 30
- 
                    3
- 
                    3orkoden , you're right. The aim of my answer is to show there is no obligation to execute two additional process. bash is self sufficient for the use case. – Emmanuel Devaux Nov 19 '13 at 08:59
- 
                    3I am using Emmanuel's method because I wish to pass either a file or a folder name, and then compute the folder path. Using this regex does the right thing, whereas the dirname function returned the parent folder when I input a folder. – AnneTheAgile Dec 07 '13 at 19:33
- 
                    12However, if there's no path info in $VAR, ${VAR%/*}/test produces an unexpected value equal to $VAR/test whereas $(dirname $VAR) will produce the more predictable and appropriate value of ./test. This is a big deal because the former will attempt to treat the filename as a directory while the latter will be OK. – davemyron Oct 01 '14 at 18:13
- 
                    1The `export` is unnecessary here. The purpose of `export` is to make the variable visible in the environment of the shell's subprocesses, but you are not calling any subprocesses which use the environment to access this information. – tripleee Aug 28 '21 at 18:03
- 
                    2This should arguably be the accepted answer. `dirname` and `basename` have their place, but if the path is already in a shell variable, using the shell's built-in facilities is more efficient and elegant than calling an external process. – tripleee Aug 28 '21 at 18:05
- 
                    1Another benefit of this is it is much MUCH faster than `dirname` I'm running it in a loop with 1000 items and dirname takes seconds whereas this is nearly instantaneous – user1169420 Nov 02 '22 at 23:24
On a related note, if you only have the filename or relative path, dirname on its own won't help. For me, the answer ended up being readlink.
fname='txtfile'    
echo $(dirname "$fname")                # output: .
echo $(readlink -f "$fname")            # output: /home/me/work/txtfile
You can then combine the two to get just the directory.
echo $(dirname $(readlink -f "$fname")) # output: /home/me/work
 
    
    - 1,203
- 15
- 15
- 
                    1if more than one path component not existed, you should use `readlink -m "$fname"` to canonicalize given name recursively – EDkan Sep 20 '16 at 12:13
If you care target files to be symbolic link, firstly you can check it and get the original file. The if clause below may help you.
if [ -h $file ]
then
 base=$(dirname $(readlink $file))
else
 base=$(dirname $file)
fi
 
    
    - 4,356
- 1
- 27
- 18
HERE=$(cd $(dirname $BASH_SOURCE) && pwd)
where you get the full path with new_path=$(dirname ${BASH_SOURCE[0]}). You change current directory with cd new_path and then run pwd to get the full path to the current directory.   
 
    
    - 2,522
- 1
- 22
- 32
- 
                    
- 
                    1This seems to be an answer to a different question actually. [The quoting is broken.](https://stackoverflow.com/questions/10067266/when-to-wrap-quotes-around-a-shell-variable) – tripleee Aug 29 '21 at 06:28
I was playing with this and came up with an alternative.
$ VAR=/home/me/mydir/file.c
$ DIR=`echo $VAR |xargs dirname`
$ echo $DIR
/home/me/mydir
The part I liked is it was easy to extend backup the tree:
$ DIR=`echo $VAR |xargs dirname |xargs dirname |xargs dirname`
$ echo $DIR
/home
 
    
    - 614
- 10
- 7
You could try something like this using approach for How to find the last field using 'cut':
Explanation
- revreverses- /home/user/mydir/file_name.cto be- c.eman_elif/ridym/resu/emoh/
- cutuses- /as the delimiter, and chooses the second field, which is- ridym/resu/emoh/, which deletes string up to the first occurrence of- /
- lastly, we reverse it again to get /home/user/mydir
$ VAR="/home/user/mydir/file_name.c"
$ echo $VAR | rev | cut -d"/" -f2- | rev
/home/user/mydir
 
    
    - 2,919
- 9
- 53
- 102
- 
                    Replacing '/' (not valid within a file name) with space (valid within a file name) is not recommended. If you must use such a solution, better to just remove everything up until, and including, the last '/' character. – Martyn Davis Dec 16 '20 at 00:10
- 
                    
- 
                    
- 
                    1@m42 updated my answer according to OP requested. Please see its updated version. – alper Jul 25 '21 at 16:05
- 
                    
- 
                    
Here is a script I used for recursive trimming. Replace $1 with the directory you want, of course.
BASEDIR=$1
IFS=$'\n'
cd "$BASEDIR"
 for f in $(find . -type f -name ' *')
 do 
    DIR=$(dirname "$f")
    DIR=${DIR:1}
    cd "$BASEDIR$DIR"
    rename 's/^ *//' *
 done
- 
                    Using a `for` loop over the output of `find` is an antipattern and a source of many bugs. This construct is inherently limited, and will fail if `find` produces results which contain whitespace or other shell metacharacters, let alone then newlines. – tripleee Aug 28 '21 at 18:08
I like my paths absolute, and I need it from the (first) bash parameter (not a variable, not $pdf, not script location):
i.e. to reliably create a subfolder next to my chosen file ($1):
rp="$(realpath $(dirname $1)/pfd_extract)"
mkdir -p "$rp"
 
    
    - 9,625
- 4
- 80
- 110
 
    