Get last dirname/filename in a file path argument in Bash
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
.
Related videos on Youtube
Comments
-
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 almost 14 yearsbasename 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 almost 14 years@tj111: sounds like is no
$1
, or$1
is empty -
George about 10 yearsOn 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 about 9 yearsThis didn't work at all when my "/path/to/whatever/" was a variable.
-
SourceSeeker about 9 years@ButtleButkus: Show me what you did and what you expected as the result, because it will work.
-
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 loopfor d in $(find $SOMEFOLDER -maxdepth 1 -type d);
Usingsubdir=$(basename $d)
works as expected. -
SourceSeeker about 9 years@ButtleButkus: You should use
while
instead offor
to iterate over the output offind
(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 youshopt -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 over 8 yearsIs there easy way to account for input path ending in
/
? -
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 almost 8 yearsunfortunately, if you wrap commands, basename is not a good idea. just something to keep in mind
-
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
containsthe 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 about 6 yearsSee here for an explanation of how and why
${1##*/}
works: unix.stackexchange.com/a/171786/15070 -
Ray Foss almost 5 yearsdoes not work with paths that have spaces... you can overcome that with quotes... which somehow works
echo "$(basename "$(dirname "$pathname")")"
-
Cognitiaclaeves almost 4 yearsThe given answer seems a little misleading. Would be better to have a little more context.