Get last dirname/filename in a file path argument in Bash

337,263

Solution 1

basename does remove the directory prefix of a path:

$ basename /usr/local/svn/repos/example
example
$ echo "/server/root/$(basename /usr/local/svn/repos/example)"
/server/root/example

Solution 2

The following approach can be used to get any path of a pathname:

some_path=a/b/c
echo $(basename $some_path)
echo $(basename $(dirname $some_path))
echo $(basename $(dirname $(dirname $some_path)))

Output:

c
b
a

Solution 3

Bash can get the last part of a path without having to call the external basename:

dir="/path/to/whatever/"
dir="${dir%/}"             # strip trailing slash (if any)
subdir="${dir##*/}"

This uses Bash's parameter expansion to remove the part of the string before the last (remaining) slash.

Solution 4

To print the file name without using external commands,

Run:

fileNameWithFullPath="${fileNameWithFullPath%/}";
echo "${fileNameWithFullPath##*/}" # print the file name

This command must run faster than basename and dirname.

Share:
337,263

Related videos on Youtube

user1260501
Author by

user1260501

I code

Updated on December 22, 2021

Comments

  • user1260501
    user1260501 over 2 years

    I'm trying to write a post-commit hook for SVN, which is hosted on our development server. My goal is to try to automatically checkout a copy of the committed project to the directory where it is hosted on the server. However I need to be able to read only the last directory in the directory string passed to the script in order to checkout to the same sub-directory where our projects are hosted.

    For example if I make an SVN commit to the project "example", my script gets "/usr/local/svn/repos/example" as its first argument. I need to get just "example" off the end of the string and then concat it with another string so I can checkout to "/server/root/example" and see the changes live immediately.

  • user1260501
    user1260501 almost 14 years
    basename is definitely what I'm looking for. How can get the basename of an argument stored into a variable though? E.g. SUBDIR="/path/to/whatever/$(basename $1)"
  • sth
    sth almost 14 years
    @tj111: sounds like is no $1, or $1 is empty
  • George
    George about 10 years
    On my Mac, using substring notation is more than order of magnitude faster than dirname / basename for the case where you're doing something trivial to each of a few thousand files.
  • Buttle Butkus
    Buttle Butkus about 9 years
    This didn't work at all when my "/path/to/whatever/" was a variable.
  • SourceSeeker
    SourceSeeker about 9 years
    @ButtleButkus: Show me what you did and what you expected as the result, because it will work.
  • Buttle Butkus
    Buttle Butkus about 9 years
    d=/home/me/somefolder;subdir="/$d/${1##*/}" I ended up with something like //home/me/somefolder// the $d actually comes from a loop for d in $(find $SOMEFOLDER -maxdepth 1 -type d); Using subdir=$(basename $d) works as expected.
  • SourceSeeker
    SourceSeeker about 9 years
    @ButtleButkus: You should use while instead of for to iterate over the output of find (find -print0 | xargs -0 is better) or use globbing: for d in $SOMEFOLDER/*/ (the final slash works like -type d - you can use ** in Bash 4 for recursion if you shopt -s globstar, but an "Argument list too long" message is possible). Note that the ${1} portion of the command represents the first argument of a script or function. You may need to use ${d##*/} or some other variable or argument specification or make sure that an argument is being passed in $1
  • Piotr Dobrogost
    Piotr Dobrogost over 8 years
    Is there easy way to account for input path ending in /?
  • SourceSeeker
    SourceSeeker over 8 years
    @PiotrDobrogost: Strip off the final / first (this has no effect if there's no trailing /). It has to be done in two steps: subdir="${1%/}"; subdir="/path/to/whatever/${1##*/}"
  • dtc
    dtc almost 8 years
    unfortunately, if you wrap commands, basename is not a good idea. just something to keep in mind
  • Vinay Vissh
    Vinay Vissh about 6 years
    @DennisWilliamson Thanks a lot for sharing. For any future readers who start to wonder why it is not working or I am the only stupid out here 😂. Above answer assumes that $1 contains the path from which last component is to be taken out. I missed that part. My use case: target_path='/home/user/dir1/dir2/dir3/'; target_path="${target_path%/}"; last_component=${target_path##*/}; echo $last_component - Works 😉
  • Matt
    Matt about 6 years
    See here for an explanation of how and why ${1##*/} works: unix.stackexchange.com/a/171786/15070
  • Ray Foss
    Ray Foss almost 5 years
    does not work with paths that have spaces... you can overcome that with quotes... which somehow works echo "$(basename "$(dirname "$pathname")")"
  • Cognitiaclaeves
    Cognitiaclaeves almost 4 years
    The given answer seems a little misleading. Would be better to have a little more context.