76

I need to remove one directory (the leftmost) from variables in Bash. I found ways how can I remove all the path or use dirname and others but it was removing all or one path component on the right side; it wouldn't help me. So you have a better understanding of what I need, I'll write an example:

I have a/project/hello.c, a/project/docs/README, ... and I want to remove that a/ so after some commands I´ll have project/hello.c and project/docs/README, ...

6 Answers 6

127

You can use any of:

x=a/b/c/d
y=a/
echo ${x#a/}
echo ${x#$y}
echo ${x#*/}

All three echo commands produce b/c/d; you could use the value in any way you choose, of course.

The first is appropriate when you know the name you need to remove when writing the script.

The second is appropriate when you have a variable that contains the prefix you need to remove (minor variant: y=a; echo ${x#$y/}).

The third is the most general - it removes any arbitrary prefix up to the first slash. I was pleasantly surprised to find that the * worked non-greedily when I tested it with bash (version 3.2) on MacOS X 10.6.6 - I'll put that down to too much Perl and regex work (because, when I think about it, * in shell doesn't include slashes).

8
  • 3
    The * can include slashes in this context. a='abc/def.ghi'; echo ${a#*.} outputs "ghi" But you're right it's not greedy when you use #, ## makes it greedy. Commented Mar 15, 2011 at 15:23
  • 1
    What do the # and ## operators actually do?
    – Pitt
    Commented Oct 12, 2012 at 8:02
  • 8
    @Pitt: RTFM? The hash operator (starting with an 'h') removes stuff from the 'head' of a variable. The pattern after the # is matched against the value of the variable, and the bit that matches (if any) is removed. With ##, the longest possible match is removed. The percent operator (with a 't' at its tail) is the complement of # and removes stuff from the tail of a variable. The % can be used to remove suffixes from file names, for example. Commented Oct 12, 2012 at 12:52
  • @JonathanLeffler: Cheers :) again learned something more about bash!
    – Pitt
    Commented Oct 12, 2012 at 13:14
  • When applying this to an array, I was surprised to see that "${array[@]/#*\/}" is greedy, and "${array[@]/##*\/}" does nothing. Commented May 7, 2015 at 10:07
15
echo a/project/hello.c | cut -d'/' -f2-
1
  • 1
    I like this, it's simple and easy to generalize. I had a similar problem and all I needed to make it work was increment the number in -f2- to -f5- for my case. Doing the same with the regular expressions given in the other solutions would have been much more difficult. Commented Dec 7, 2018 at 15:51
6
echo "a/project/hello.c" | sed 's,^[^/]*/,,'
2

Look at man expr

expr "foo/bar/baz" : '[^/]*/\(.*\)' will do what you want.

0

You can pipe to xargs and do new replacements

How I used it to rename all files in directories under ts-src from ts to js and tsx to jsx to same subdirectories.

find ts-src/ -type f -not -name "*.tsx" -not -name "*.ts" \
  -exec bash -c 'echo "${0##*/}" "${0##ts-src/}" "$0"' {} \; | \
    xargs -l bash -c 'echo mkdir -p "src/${1%$0}" && echo cp "$2" "src/$0"'
-3

None of the examples above solved my problem. I wanted to be able to switch java versions by changing the $PATH value. After googling and cannot find sufficient answer, I weaved my own solution below.

Here is an excerpt in my .bashrc:

jv8() {
    export JAVA_HOME=/opt/jdk1.8.0_121
    y=$(echo $PATH | tr ':' '\n' |sed '/\/opt\/jdk/d' | tr '\n' ':')
    export PATH=$JAVA_HOME/bin:$y
}

jv6() {
    export JAVA_HOME=/opt/jdk1.6.0_45
    y=$(echo $PATH | tr ':' '\n' |sed '/\/opt\/jdk/d' | tr '\n' ':')
    export PATH=$JAVA_HOME/bin:$y
}

So, in my bash shell, I can toggle simply by:

$> jv6
java -version
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)
$> jv8
java -version
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

Not the answer you're looking for? Browse other questions tagged or ask your own question.