I'm trying to remove part of the path in a string. I have the path:
/path/to/file/drive/file/path/
I want to remove the first part /path/to/file/drive
and produce the output:
file/path/
Note: I have several paths in a while loop, with the same /path/to/file/drive
in all of them, but I'm just looking for the 'how to' on removing the desired string.
I found some examples, but I can't get them to work:
echo /path/to/file/drive/file/path/ | sed 's:/path/to/file/drive:\2:'
echo /path/to/file/drive/file/path/ | sed 's:/path/to/file/drive:2'
\2
being the second part of the string and I'm clearly doing something wrong...maybe there is an easier way?
You can also use POSIX shell variable expansion to do this.
path=/path/to/file/drive/file/path/
echo ${path#/path/to/file/drive/}
The #..
part strips off a leading matching string when the variable is expanded; this is especially useful if your strings are already in shell variables, like if you're using a for
loop. You can strip matching strings (e.g., an extension) from the end of a variable also, using %...
. See the bash
man page for the gory details.
If you wanted to remove a certain NUMBER of path components, you should use cut
with -d'/'
. For example, if path=/home/dude/some/deepish/dir
:
To remove the first two components:
# (Add 2 to the number of components to remove to get the value to pass to -f)
$ echo $path | cut -d'/' -f4-
some/deepish/dir
To keep the first two components:
$ echo $path | cut -d'/' -f-3
/home/dude
To remove the last two components (rev
reverses the string):
$ echo $path | rev | cut -d'/' -f4- | rev
/home/dude/some
To keep the last three components:
$ echo $path | rev | cut -d'/' -f-3 | rev
some/deepish/dir
Or, if you want to remove everything before a particular component, sed
would work:
$ echo $path | sed 's/.*\(some\)/\1/g'
some/deepish/dir
Or after a particular component:
$ echo $path | sed 's/\(dude\).*/\1/g'
/home/dude
It's even easier if you don't want to keep the component you're specifying:
$ echo $path | sed 's/some.*//g'
/home/dude/
And if you want to be consistent you can match the trailing slash too:
$ echo $path | sed 's/\/some.*//g'
/home/dude
Of course, if you're matching several slashes, you should switch the sed
delimiter:
$ echo $path | sed 's!/some.*!!g'
/home/dude
Note that these examples all use absolute paths, you'll have to play around to make them work with relative paths.
If you don't want to hardcode the part you're removing:
$ s='/path/to/file/drive/file/path/'
$ echo ${s#$(dirname "$(dirname "$s")")/}
file/path/
One way to do this with sed is
echo /path/to/file/drive/file/path/ | sed 's:^/path/to/file/drive/::'
Using ${path#/path/to/file/drive/}
as suggested by evil otto is certainly the typical/best way to do this, but since there are many sed suggestions it is worth pointing out that sed is overkill if you are working with a fixed string. You can also do:
echo $PATH | cut -b 21-
To discard the first 20 characters. Similarly, you can use ${PATH:20}
in bash or $PATH[20,-1]
in zsh.
If you want to remove the first N parts of the path, you could of course use N calls to basename
, as in glenn's answer, but it's probably easier to use globbing:
path=/path/to/file/drive/file/path/
echo "${path#*/*/*/*/*/}" # file/path/
Specifically, ${path#*/*/*/*/*/}
means "return $path
minus the shortest prefix that contains 5 slashes".
Pure bash, without hard coding the answer
basenames()
{
local d="${2}"
for ((x=0; x<"${1}"; x++)); do
d="${d%/*}"
done
echo "${2#"${d}"/}"
}
- Argument 1 - How many levels do you want to keep (2 in the original question)
- Argument 2 - The full path