2

I have the path to a directory, like this:

DIRNAME=/path/to/projects/proj1/dir1

I want strip everything after projects, but keep projects in the file path. However, this code does not work:

DIRNAME=/path/to/projects/proj1/dir1
My_Dir=projects

echo ${DIRNAME%$My_Dir*}

This returns /path/to/; I want to return /path/to/projects instead. Nothing after projects in the file path is static and thus cannot be used in the substring match as per this example.

Obligatory sidenote: I have read over a dozen different pages with multiple examples each of substring manipulation but have not been able to find one that shows how to do this. I think sed might be able to do something like this but I haven't seen any examples that do this.

3
  • 1
    My usual solution in this kind of scenario is to just do something like this: ${DIRNAME%$My_Dir*}/$My_Dir Commented Dec 16, 2015 at 0:47
  • 1
    Maybe you can use ${DIRNAME%$proj1*}, so this would return /path/to/projects/ Commented Dec 16, 2015 at 0:50
  • ...by the way -- this may or may not be entirely under your control, but using all-caps names for your own variables is contrary to POSIX-established conventions (pubs.opengroup.org/onlinepubs/009695399/basedefs/…, see fourth paragraph), which reserve all-caps names for system use and suggest that application-controlled names should have at least one lower-case character. Commented Dec 16, 2015 at 1:11

3 Answers 3

2

This is generally how I would pull apart directories in this kind of situation:

DIRNAME=/path/to/projects/proj1/dir1
My_Dir=projects

PROJROOT=${DIRNAME%$My_Dir*}$My_Dir
PROJDIR=${DIRNAME#*$PROJROOT}

echo $DIRNAME
echo $PROJROOT
echo $PROJDIR

The output for this is:

/path/to/projects/proj1/dir1
/path/to/projects
/proj1/dir1
Sign up to request clarification or add additional context in comments.

Comments

2

You need to be careful, but you can make the shell substitution work. For example:

keep="projects"
base="${DIRNAME%/$keep/*}/$keep"

Note that the /$keep/ part ensures that you get the right path with /path/to/projects/with/remote_projects_here/to/confuse/you; your original would mismanage that string.

You can also do it with sed:

base=$(sed "s%\(.*/$keep\)/.*%\1%" <<< "$DIRNAME")

using a here string; or you could do it the portable and classic way with echo:

base=$(echo "$DIRNAME" | sed "s%\(.*/$keep\)/.*%\1%")

Of course, you can still run into problems with what's in the sub-directory names:

/path/to/projects/with/remote_projects_here/projects/select

Test code:

#!/bin/bash

My_Dir=projects
keep="projects"
for dir in \
    /path/to/projects/proj1/dir1 \
    /path/to/projects/with/remote_projects_here/to/confuse/you
do
    DIRNAME="$dir"

    echo "Data"
    echo "$DIRNAME"

    echo "Original"
    echo "${DIRNAME%$My_Dir*}"

    echo "Variant 1"
    base="${DIRNAME%/$keep/*}/$keep"
    echo "$base"

    echo "Variant 2"
    base=$(sed "s%\(.*/$keep\)/.*%\1%" <<< "$DIRNAME")
    echo "$base"

    echo "Variant 3"
    base=$(echo "$DIRNAME" | sed "s%\(.*/$keep\)/.*%\1%")
    echo "$base"
done

Example output:

Data
/path/to/projects/proj1/dir1
Original
/path/to/
Variant 1
/path/to/projects
Variant 2
/path/to/projects
Variant 3
/path/to/projects
Data
/path/to/projects/with/remote_projects_here/to/confuse/you
Original
/path/to/projects/with/remote_
Variant 1
/path/to/projects
Variant 2
/path/to/projects
Variant 3
/path/to/projects

The output could be better formatted, but it makes the point.

Comments

0
DIR=/path/to/projects/proj1/dir1 

dirname "$DIR"
basename "$DIR"

outputs

/path/to/projects/proj1
dir1 

so you can concatenate these with a slash, and you're done. I think the simplest way is this.

12 Comments

Quotes -- basename "$DIR", dirname "$DIR".
Try testing again with directory names that contain spaces.
...and that's only one pathological case out of many. For instance, one can run mkdir '*' to create a directory named *; you might look at how your code works (without the quotes) with that, too.
I didn't understand your last example but, I tried this DIR=/path/to/projects\ /proj1/dir1, after projects it has a space, but it's working. I really couldn't see why it's not working with spaces, can you please give an example input? So I can learn.
...and when I run DIR=/path/to/projects\ /proj1/dir1; dirname $DIR on a Linux system with GNU tools, I get two separate lines of output, /path/to and /proj1. Which is what I expect, since the shell is breaking the expanded value into two arguments and passing them separately.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.