115

Suppose I have a file /from/here/to/there.txt, and want to get only the last part of its dirname to instead of /from/here/to, what should I do?

codeforester
  • 39,467
  • 16
  • 112
  • 140
eggplantelf
  • 1,342
  • 2
  • 9
  • 7

9 Answers9

166

You can use basename even though it's not a file. Strip off the file name using dirname, then use basename to get the last element of the string:

dir="/from/here/to/there.txt"
dir="$(dirname $dir)"   # Returns "/from/here/to"
dir="$(basename $dir)"  # Returns just "to"
Jeff Y
  • 2,437
  • 1
  • 11
  • 18
David W.
  • 105,218
  • 39
  • 216
  • 337
28

The opposite of dirname is basename:

basename "$(dirname "/from/here/to/there.txt")"
that other guy
  • 116,971
  • 11
  • 170
  • 194
24

Using bash string functions:

$ s="/from/here/to/there.txt"
$ s="${s%/*}" && echo "${s##*/}"
to
jaypal singh
  • 74,723
  • 23
  • 102
  • 147
15

Using Bash parameter expansion, you could do this:

path="/from/here/to/there.txt"
dir="${path%/*}"       # sets dir      to '/from/here/to' (equivalent of dirname)
last_dir="${dir##*/}"  # sets last_dir to 'to' (equivalent of basename)

This is more efficient since no external commands are used.

codeforester
  • 39,467
  • 16
  • 112
  • 140
  • I just tested this. On FreeBSD here are my results: Code: #!/bin/sh cd /var/svn/repos path="$1" echo "${path}" dir="${path%/*}" echo "${dir}" actual="${dir##*/}" echo "${actual}" ---------------------------------------- Results: $ ./makerepo.sh /var/test /var/test /var var $ ------------------------------------ Fail! Sorry. Seemed a good method but not a correct answer. – Richard Robertson Nov 18 '17 at 16:04
4

Pure BASH way:

s="/from/here/to/there.txt"
[[ "$s" =~ ([^/]+)/[^/]+$ ]] && echo "${BASH_REMATCH[1]}"
to
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 2
    Will not work if file has no dot `.`. Will not work if directory has dot `.`. Keep it simple and use slash `/` as delimiter. – alvits Apr 18 '14 at 21:44
4

An awk way of doing it would be:

awk -F'/' '{print $(NF-1)}' <<< "/from/here/to/there.txt"

Explanation:

  • -F'/' sets field separator as "/"
  • print the second last field $(NF-1)
  • <<< uses anything after it as standard input (wiki explanation)
csiu
  • 3,159
  • 2
  • 24
  • 26
2

One more way

IFS=/ read -ra x <<<"/from/here/to/there.txt" && printf "%s\n" "${x[-2]}"
iruvar
  • 22,736
  • 7
  • 53
  • 82
2

This question is something like THIS.

For solving that you can do:

DirPath="/from/here/to/there.txt"
DirPath="$(dirname $DirPath)"
DirPath="$(basename $DirPath)"

echo "$DirPath"

As my friend said this is possible as well:

basename `dirname "/from/here/to/there.txt"`

In order to get any part of your path you could do:

echo "/from/here/to/there.txt" | awk -F/ '{ print $2 }'
OR
echo "/from/here/to/there.txt" | awk -F/ '{ print $3 }'
OR
etc
Community
  • 1
  • 1
MLSC
  • 5,872
  • 8
  • 55
  • 89
1

The top answer is absolutely correct for the question asked. In a more generic case with the needed directory in the middle of a long path, this approach leads to a hard to read code. For example :

dir="/very/long/path/where/THIS/needs/to/be/extracted/text.txt"
dir="$(dirname $dir)"
dir="$(dirname $dir)"
dir="$(dirname $dir)"
dir="$(dirname $dir)"
dir="$(dirname $dir)"
dir="$(basename $dir)"

In this case one can use:

IFS=/; set -- "/very/long/path/where/THIS/needs/to/be/extracted/text.txt"; set $1; echo $6
THIS
yashma
  • 335
  • 2
  • 6