intelfx
intelfx

Reputation: 2764

sed-ish oneliner to perform arithmetic within substitution

I have a string of form FOO_123_BAR.bazquux, where FOO and BAR are fixed strings, 123 is a number and bazquux is freeform text.

I need to perform a text transformation on this string: extract 123 and bazquux, increment the number and then arrange them in a different string.
For example, FOO_123_BAR.bazquuxFOO=124 BAR=bazquux. (Actual transformation is more complex.)

Naturally, I can do this in a sequence of sed and expr calls, but it's ugly:

shopt -s lastpipe

in=FOO_123_BAR.bazquux
echo "$in" | sed -r 's|^FOO_([0-9]+)_BAR\.(.+)$|\1 \2|' | read number text
out="FOO=$((number + 1)) BAR=$text"

Is there a more powerful text processing tool that can do the job in a single invocation? If yes, then how?


Edit: I apologize for not making this clearer, but the exact structure of the input and output is an example. Thus, I prefer general solutions that work with any delimiters or absence thereof, rather than solutions that depend on e. g. presence of underscores.

Upvotes: 5

Views: 153

Answers (6)

M. Nejat Aydin
M. Nejat Aydin

Reputation: 10133

A pure bash one-liner would be

[[ $s =~ FOO_([0-9]+)_BAR\.(.*) ]] && echo "FOO=$((BASH_REMATCH[1] + 1)) BAR=${BASH_REMATCH[2]}"

assuming the variable s is set to the string that is being parsed before calling that line (s=FOO_123_BAR.bazquux).

Upvotes: 2

Ed Morton
Ed Morton

Reputation: 204015

Using any awk in any shell on every UNIX box and assuming none of your substrings contain _ or .:

$ s='FOO_123_BAR.bazquux'
$ echo "$s" | awk -F'[_.]' '{print $1"="$2+1,$3"="$4}'
FOO=124 BAR=bazquux

Upvotes: 5

RavinderSingh13
RavinderSingh13

Reputation: 133640

Could you please try following, written and tested with shown samples in GNU awk.

1st solution: Adding solution with match function awk.

echo "FOO_123_BAR.bazquux" | 
awk '
match($0,/FOO_[0-9]+_BAR/){
  split(substr($0,RSTART,RLENGTH),array,"_")
  print array[1]"="array[2]+1,array[3] "=" substr($0,RSTART+RLENGTH+1)
}'


2nd solution:

echo "FOO_123_BAR.bazquux" | 
awk '
BEGIN{
  FS="_"
}
{
  $2+=1
  sub(/_/,"=")
  sub(/_/," ")
  sub(/\./,"=")
}
1'

Upvotes: 3

Ivan
Ivan

Reputation: 7287

Using var substitution:

in=FOO_123_BAR.bazquux
raw=(${in//_/ })
$ echo "$raw=$[raw[1]+1] ${raw[2]//./=}"
FOO=124 BAR=bazquux

Upvotes: 1

Sundeep
Sundeep

Reputation: 23677

With GNU sed, you can execute the entire replacement string as an external command using the e flag.

$ s='FOO_123_BAR.bazquux'
$ echo "$s" | sed -E 's/^FOO_([0-9]+)_BAR\.(.+)$/echo FOO=$((\1 + 1)) BAR=\2/e'
FOO=124 BAR=bazquux

To avoid conflict with shell metacharacters, you need to quote the unknown portions:

$ s='FOO_123_BAR.$x(1)'
$ echo "$s" | sed -E 's/^FOO_([0-9]+)_BAR\.(.+)$/echo FOO=$((\1 + 1)) BAR=\2/e'
sh: 1: Syntax error: "(" unexpected

$ echo "$s" | sed -E 's/^FOO_([0-9]+)_BAR\.(.+)$/echo FOO=$((\1 + 1)) BAR=\x27\2\x27/e'
FOO=124 BAR=$x(1)

Upvotes: 6

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 627103

You may do it with perl:

perl -pe 's|^FOO_([0-9]+)_BAR\.(.+)$|"FOO=" . ($1 + 1) . " BAR=" . $2|e' <<< "$in"

See the online demo

The ($1 + 1) will increment the number captured in Group 2.

Upvotes: 4

Related Questions