Reputation: 11730
In bash, there are several useful string manipulation patterns, such as removing shortest/longest substring from beginning/end of the string:
${var#substring}
${var##substring}
${var%substring}
${var%%substring}
Then there is also the replacement pattern, that replaces a substring from any part of the string:
${var/substring/replacement}
The problem with this is that it is greedy and always replaces the longest match. For example if I have a directory name like /a/b/foo-bar/x/y/z
and I want to replace any subdirectory name starting with foo- to baz, then it won't work as I expect. I expect the result to be /a/b/baz/x/y/z
. I tried the following command:
${PWD/\/foo-*\///baz/}
The result in this case is /a/b/baz/z
, because the pattern matches the longest substring starting with /foo-
and ending with /
. Is there any way to get the correct result without calling sed
or any other external string manipulation program?
Upvotes: 3
Views: 1903
Reputation: 785581
In pure BASH you can do this (using BASH regex capabilities):
s='/a/b/foo-bar/x/y/z'
p="$s"
[[ "$s" =~ ^(.*/)'foo-'[^/]*(.*)$ ]] && p="${BASH_REMATCH[1]}baz${BASH_REMATCH[2]}"
echo "$p"
/a/b/baz/x/y/z
Upvotes: 2
Reputation: 46853
Of course, you can always use extended globs:
shopt -s extglob
var=/a/b/foo-bar/x/y/z/foo-bar2/1/2/3
echo "${var//\/foo-*([^\/])\///baz/}"
will happily output
/a/b/baz/x/y/z/baz/1/2/3
Upvotes: 4
Reputation: 55489
Here's a way to do it in pure Bash that replaces all subdirectories in the path that match the pattern. It splits the path into an array, does the replacement on each path component into a new array, and then uses printf
to replace the path separators.
name='/a/b/foo-bar/x/foo-y/z';
IFS=$'/';
aname=($name);
bname=();
for i in "${aname[@]}";
do bname+=("${i/foo-*/baz}");done;
printf -v newname "%s/" "${bname[@]}";
printf "%s\n" "$newname"
output
/a/b/baz/x/baz/z/
(Sorry about all the ;
s, I just did a quick copy & paste from the shell)
Upvotes: 1
Reputation: 195179
you cannot use regex in bash's built-in string substitution. If you have to stick to the built-in string manipulation, you have to extract the substring foo-bar
first then use it in your ${PWD/$match/baz}
.
You can do it very easily however, if you could use regex. There are a lot of handy string handlers under linux/unix, could do that very easily. sed for example:
kent$ sed 's#/foo-[^/]*#/baz#' <<< '/a/b/foo-bar/x/y/z'
/a/b/baz/x/y/z
Upvotes: 0