GillesF
GillesF

Reputation: 21

sed digits with optional decimal and backreference

I have simple input like this :

11111(n)
222222(p)
33333333(:)

I can use sed backreference to swap parenthesis with digits like this :

sed -e 's/\([[:digit:]]*\)\((.*)\)/\2 \1/' file

which produce

(n) 11111
(p) 222222
(:) 33333333

It's cool !

But things became more difficult with potential decimal digits, like this

11111(n)
11111.111(n)
2222222.22(p)
33.3333333(:)

I've try many commands, like

sed -e 's/\([[:digit:]]*(\.[[:digit:]]*?)\)\((.*)\)/\2 \1/' file
sed -e 's/\([[:digit:]]*\.?[[:digit:]]*?)\)\((.*)\)/\2 \1/' file
sed -e 's/\([[:digit:]]*\.*[[:digit:]]*)\)\((.*)\)/\2 \1/' file
sed -e 's/\([[:digit:]]*.*[[:digit:]]*)\)\((.*)\)/\2 \1/' file

Output desired :

(n) 11111
(n) 11111.111
(p) 2222222.22
(:) 33.3333333

Note that digits can be arbitrary long (1 to n digits), and decimal mark (.) and decimal digits are optional.

Furthermore, sed don't seem to have \d shorthand, as pointed in stackexchange

Upvotes: 1

Views: 859

Answers (3)

Tyl
Tyl

Reputation: 5252

Why not just use simple collection?

sed -e 's/\([0-9.]*\)\((.*)\)/\2 \1/' file

Since [0-9] and [:digit:] are basically the same, but when you want include other character, former one is more intuitive.

Given a second thought, in which I see that you were trying to match the legitimate number, i.e. no point or only one point, thus improved sed one would be:

sed -r 's/([0-9]+(\.[0-9]+)?)(\(.*\))/\3 \1/' file

-r to support +? in RE and switch parentheses' escape around.
Or use perl to avoid all those RE extension confusion:

perl -lne 'print "$3 $1" if /(\d+(\.\d+)?)\s*(\(.*?\))/' file

Update: As Benjamin W. mentioned in comments, [0-9] and [[:digit:]] are not the same, so if you want to consider possible digits in other languages, the sed one should be:

sed -r 's/([[:digit:]]+(\.[[:digit:]]+)?)(\(.*\))/\3 \1/' file

Upvotes: 0

Inian
Inian

Reputation: 85530

It gets quite simple when you know what to match in the POSIX bracket expression with [:digit:]. All you need to do is include another . so that the bracket expression would mean set of digits along with .,

sed 's/\([[:digit:].]*\)\((.*)\)/\2 \1/' file

Also you don't need to mention -e, because sed by default operates in BRE (Basic Regular Expressions) mode and with -E the ERE (Extended Regular Expression) mode is enabled. Also \d is not a valid regular expression construct used by any versions of sed (POSIX, GNU or FreeBSD) to match digits. I suppose it is supported in the PCRE library in which you could use perl

perl -lne 'print "$2 $1" if /(\d+\.?\d*).*(\([^)]*\))/' file

Upvotes: 2

anubhava
anubhava

Reputation: 784898

You may use this sed:

sed -E 's/^([.[:digit:]]+)(\([^)]*\))/\2 \1/g' file

(n) 11111
(n) 11111.111
(p) 2222222.22
(:) 33.3333333

Here [.[:digit:]]+ will match 1+ of any digit or dot character.

Upvotes: 1

Related Questions