• Uncategorized

About regex : Sed-can-not-replace-part-of-the-string-whithout-replacing-all-of-it

Question Detail

I am trying to replace part of the string, but can not find a proper regex for sed to execute it properly.

I have a string
/abc/foo/../bar
And I would like to achive the following result:
/abc/bar

I have tried to do it using this command:

echo $string | sed 's/\/[^:-]*\..\//\//'

But as result I am getting just /bar.

I understand that I must use group, but I just do not get it.
Could you, please, help me to find out this group that could be used?

Question Answer

You can use

#!/bin/bash
string='/abc/foo/../bar'
sed -nE 's~^(/[^/]*)(/.*)?/\.\.(/[^/]*).*~\1\3~p' <<< "$string"

See the online demo. Details:

  • -n – suppresses default line output
  • E – enables POSIX ERE regex syntax
  • ^ – start of string
  • (/[^/]*) – Group 1: a / and then zero or more chars other than /
  • (/.*)? – an optional group 2: a / and then any text
  • /\.\. – a /.. fixed string
  • (/[^/]*) – Group 3: a / and then zero or more chars other than /
  • .* – the rest of the string.
  • \1\3 replaces the match with Group 1 and 3 values concatenated
  • p only prints the result of successful substitution.

You can use a capture group for the first part and then match until the last / to remove.

As you are using / to match in the pattern, you can opt for a different delimiter.

#!/bin/bash
string="/abc/foo/../bar"

sed 's~\(/[^/]*/\)[^:-]*/~\1~' <<< "$string"

The pattern in parts:

  • \( Capture group 1
    • /[^/]*/ Match from the first till the second / with any char other than / in between
  • \) Close group 1
  • [^:-]*/ Match optional chars other than : and - then match /

Output

/abc/bar

Using sed

$ sed 's#^\(/[^/]*\)/.*\(/\)#\1\2#' input_file
/abc/bar

or

$ sed 's#[^/]*/[^/]*/##2' input_file
/abc/bar

Using awk

string='/abc/foo/../bar'
awk -F/ '{print "/"$2"/"$NF}' <<< "$string"
#or
awk -F/ 'BEGIN{OFS=FS}{print $1,$2,$NF}' <<< "$string"

/abc/bar

Using bash

string='/abc/foo/../bar'
echo "${string%%/${string#*/*/}}/${string##*/}"

/abc/bar

Using any sed:

$ echo "$string" | sed 's:\(/[^/]*/\).*/:\1:'
/abc/bar

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.