Sasha
Sasha

Reputation: 301

Extracting a string between last two slashes in Bash

I know this can be easily done using regex like I answered on https://stackoverflow.com/a/33379831/3962126, however I need to do this in bash.

So the closest question on Stackoverflow I found is this one bash: extracting last two dirs for a pathname, however the difference is that if

DIRNAME = /a/b/c/d/e

then I need to extract

d

Upvotes: 10

Views: 9100

Answers (6)

Shelvacu
Shelvacu

Reputation: 4360

Using awk:

echo "/a/b/c/d/e" | awk -F / '{ print $(NF-1) }' # d

Edit: This does not work when the path contains newlines, and still gives output when there are less than two slashes, see comments below.

Upvotes: 3

Charles Duffy
Charles Duffy

Reputation: 295698

This may be relatively long, but it's also much faster to execute than most preceding answers (other than the zsh-only one and that by j.a.), since it uses only string manipulations built into bash and uses no subshell expansions:

string='/a/b/c/d/e'  # initial data
dir=${string%/*}     # trim everything past the last /
dir=${dir##*/}       # ...then remove everything before the last / remaining
printf '%s\n' "$dir" # demonstrate output

printf is used in the above because echo doesn't work reliably for all values (think about what it would do on a GNU system with /a/b/c/-n/e).

Upvotes: 13

whoan
whoan

Reputation: 8531

Here a pure bash solution:

[[ $DIRNAME =~ /([^/]+)/[^/]*$ ]] && printf '%s\n' "${BASH_REMATCH[1]}"

Compared to some of the other answers:

  • It matches the string between the last two slashes. So, for example, it doesn't match d if DIRNAME=d/e.
  • It's shorter and fast (just uses built-ins and doesn't create subprocesses).
  • Support any character between last two slashes (see Charles Duffy's answer for more on this).

Also notice that is not the way to assign a variable in bash:

DIRNAME = /a/b/c/d/e
       ^ ^

Those spaces are wrong, so remove them:

DIRNAME=/a/b/c/d/e

Upvotes: 4

Sasha
Sasha

Reputation: 301

OMG, maybe this was obvious, but not to me initially. I got the right result with:

dir=$(basename -- "$(dirname -- "$str")")
echo "$dir"

Upvotes: 1

Jose Ricardo Bustos M.
Jose Ricardo Bustos M.

Reputation: 8174

Using sed

if you want to get the fourth element

DIRNAME="/a/b/c/d/e"
echo "$DIRNAME" | sed -r 's_^(/[^/]*){3}/([^/]*)/.*$_\2_g'

if you want to get the before last element

DIRNAME="/a/b/c/d/e"
echo "$DIRNAME" | sed -r 's_^.*/([^/]*)/[^/]*$_\1_g'

Upvotes: 1

fivetentaylor
fivetentaylor

Reputation: 1297

Using zsh parameter substitution is pretty cool too

echo ${${DIRNAME%/*}##*/}

I think it's faster than the double $() as well, because it won't need any subprocesses.

Basically it slices off the right side first, and then all the remaining left side second.

Upvotes: 0

Related Questions