# How to replace character in capture group with sed

Given a pattern of the form:

a\b\c\d:text\text


how can I use sed to output

a/b/c/d:text\text


that is, given a path using backslashes followed by a colon followed by arbitrary text, how do I replace only the backslashes preceding the colon with forward slashes?

• So, the a\b\c\d is just an example? The path (the string to the left of the colon) could be \a\b or e\f\g\h\j\k\l? Aug 23 '17 at 2:42

## 2 Answers

Using awk rather than sed:

awk -v FS=':' -v OFS=':' '{ gsub(/\\/,"/",$1) } { print }'  This treats the data as a :-delimited record and uses gsub() to replace all backslashes with forward slashes in the first field. It then outputs the modified data. I set both the input field separator (FS) and the output field separator (OFS) to : so that the input is split on the colon and so that the colon is later inserted again when the output happens. If you have the text in a shell variable, you may do this directly in the shell without using any external utilities too: a='a\b\c\d:text\text' b=${a%%:*}  # the start of the string ("remove the first : and everything after it from $a") c=${a#*:}   # the end of the string ("remove the first : and everything before it from $a") a="${b//\\//}:$c" printf '%s\n' "$a"


This code snippet will print the string a/b/c/d:text\text in any sh-shell that supports the parameter expansion ${parameter//pattern/string} (an extension to the POSIX standard). This is more or less mimicking the awk code. • With your Awk script in single quotes as shown, you should not need quadruple backslashing ever. The shell will not try to interpret or further parse anything in single quotes. Can you double check what happened? Aug 23 '17 at 6:44 • @Wildcard That's what I thought too. So the reason I'm repeating it 4 times is that I get the wrong results (except with mawk). That means that it might be something else other than the shell that requires the extra quoting in gawk and nawk. Aug 23 '17 at 6:47 • Ah, digging through the POSIX specs, I see that "all string literals are subject to lexical analysis, which would reduce each pair of <backslash> characters to a single <backslash> before being passed to gsub." So you're running into this because you're passing a string literal as a pattern rather than a literal ERE. Using gsub(/\\/,"/",$1) should work on any Awk. (I'd love it if you could verify; I don't have all those versions you mention.) Aug 23 '17 at 6:49
• @Wildcard Nicely! Thanks for that! Turns out mawk was doing it wrong then, which is odd. I don't know why I used a string literal there... Aug 23 '17 at 6:53
• (1) Regarding backslashes in gsub calls in awk, see The GNU Awk User’s Guide Gory Details — More about ‘\’ and ‘&’ with sub(), gsub(), and gensub(). (2) In your second solution, I believe that you need b=\${a%%:*}, because the delimiter is (presumably) the first colon.  Since the OP says that the text after the colon delimiter is arbitrary, it could include additional colon(s); e.g., a\b\c\d:foo:bar. Aug 23 '17 at 20:20

A few ways to do this; here is one:

sed -e ':a' -e 's_^$$[^:]*$$\\_\1/_;t a'


Explanation breadcrumbs:

-e specifies an editing command. There are two such commands here.

:a defines a label which can be used in "goto" statements.

The s command replaces the first backslash on the line with a forward slash, as long as the first backslash occurs before any colon character.

The t a command branches to the :a label providing a substitution was successfully made with the s command. (Conditional "goto.")

• @Scott, if you check my profile and look at my Sed and Awk answers, you will see that I am sometimes given to very verbose explanations. (Example.) But, I don't feel obliged to write those in every instance. I will add a brief explanation, since you asked; feel free to expand it. Aug 23 '17 at 3:07