336

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.

jww
  • 97,681
  • 90
  • 411
  • 885
TJ L
  • 23,914
  • 7
  • 59
  • 77

5 Answers5

493

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
eduardomozart
  • 1,444
  • 15
  • 14
sth
  • 222,467
  • 53
  • 283
  • 367
  • 2
    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)"` – TJ L Jul 20 '10 at 20:38
  • 6
    @tj111: sounds like is no `$1`, or `$1` is empty – sth Jul 20 '10 at 20:59
  • 1
    unfortunately, if you wrap commands, basename is not a good idea. just something to keep in mind – dtc Jul 20 '16 at 20:36
149

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
Captain Man
  • 6,997
  • 6
  • 48
  • 74
Jingguo Yao
  • 7,320
  • 6
  • 50
  • 63
  • 5
    does not work with paths that have spaces... you can overcome that with quotes... which somehow works `echo "$(basename "$(dirname "$pathname")")"` – Ray Foss Aug 15 '19 at 14:52
97

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.

Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • 3
    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. – George Jun 26 '14 at 01:24
  • 6
    @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 – Vinay Vissh Apr 13 '18 at 11:10
  • 3
    See here for an explanation of *how* and *why* `${1##*/}` works: https://unix.stackexchange.com/a/171786/15070 – Matt Apr 15 '18 at 10:50
2

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.

Mostafa Wael
  • 2,750
  • 1
  • 21
  • 23
-1

If the file is in the current directory, you will invite this short:

cd /dev/shm
basename $(pwd) # shm

I already have had this sed command, although I admit it is not much practical:

path="/dev/shm"
sed -r "s/^.*\/(.*)$/\1/" <<< $path # shm
xerostomus
  • 466
  • 4
  • 11