0

I want to replace values like this: $SOME_TEXT_HERE$ with values like this: @some.text.here@ That is:

  1. Change text to lowercase
  2. Replace $ with @
  3. Replace underscores with dots

There may be any amount of unrelated text in the initial string. There may be any amount of dollar-delimited text to replace. Dollar-delimited text may consist of one or more capitalized words separated with underscores.

I know how to do steps 1 and 2. I want to know how to limit step 3 to content that is inside dollar signs.

Ok, an example:

From: Lorem $IPSUM$ $dolor_sit_amet _ _ _ consectetur $ADIPISICING_ELIT$ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ $ET_DOLORE_MAGNA_ALIQUA$

To: Lorem @ipsum@ $dolor_sit_amet _ _ _ consectetur @adipisicing.elit@ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ @et.dolore.magna.aliqua@

I have got this far: echo 'Lorem $IPSUM$ $dolor_sit_amet _ _ _ consectetur $ADIPISICING_ELIT$ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ $ET_DOLORE_MAGNA_ALIQUA$ ' |sed -e 's/\$\([A-Z]\+_\?\)\+\$/\L&/g' -e's/\$\(\([a-z]\+_\?\)\+\)\$/@\1@/g'

which gives: Lorem @ipsum@ $dolor_sit_amet _ _ _ consectetur @adipisicing_elit@ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ @et_dolore_magna_aliqua@

Don't know how to iterate over matched group results to grab all the words within @ signs. Always get the last matched result.

7
  • 1
    Have you tried anything ? Its better to add sample input + expected output for clarity Jun 25, 2020 at 12:39
  • 2
    Please edit your question and give us i) an example input file that contains at least one of each of the many cases you need to deal with and ii) the output you want from that example. We also need to know about edge cases, can you have things like $foo$bar$baz? So an odd number of dollar signs? Multiple sets on the same line?
    – terdon
    Jun 25, 2020 at 12:39
  • @terdon, ok, updated
    – svz
    Jun 25, 2020 at 13:25
  • The text $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ is not modified because it contains a space?
    – Kusalananda
    Jun 25, 2020 at 13:37
  • @Kusalananda, you are right
    – svz
    Jun 25, 2020 at 13:37

6 Answers 6

2

Using Perl:

$ cat file
Lorem $IPSUM$ $dolor_sit_amet _ _ _ consectetur $ADIPISICING_ELIT$ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ $ET_DOLORE_MAGNA_ALIQUA$
$ perl -pe 's/\$([[:upper:]_]+)\$/"@" . lc $1 =~ tr[_][.]r . "@"/eg' file
Lorem @ipsum@ $dolor_sit_amet _ _ _ consectetur @adipisicing.elit@ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ @et.dolore.magna.aliqua@

The Perl expression used here is

s/\$([[:upper:]_]+)\$/"@" . lc $1 =~ tr[_][.]r . "@"/eg

It's a substitution that is applied to substrings consisting of an initial $ followed by one or more upper-case characters or underscores, followed by another $ character.

The replacement pattern is a Perl expression that changes all underscores to dots in the substring that matches the parenthesized group in the pattern ($1) using the tr operator, and then lower-cases the result using lc. The result then has @ character prepended and appended.

It is the /e flag to the s/// expression that makes Perl treat the replacement text as a Perl expression rather than as just text. The substitution is repeated for all matches using the /g flag.

0
1

If the input was stored in a $string variable in the zsh shell, you could do:

set -o extendedglob
string=${string//(#b)\$([A-Z]##(_[A-Z]##)#)\$/@${(L)match[1]//_/.}@}

Same as perl's

string=$(
  printf '%s\n' "$string" | perl -pe '
    s{\$[A-Z]+(_[A-Z]+)*\$}{lc$& =~ y/_$/.@/r}ge'
)

Or:

string=$(
  printf '%s\n' "$string" | perl -pe '
    s{\$[A-Z]+(_[A-Z]+)*\$}{$& =~ y/A-Z_$/a-z.@/r}ge'
)

Excepts that perl works at byte level (expecting ASCII encoding on ASCII-based systems) while zsh works at character level (falls back to byte when bytes can't be decoded into characters) as per the locale's encoding. The behaviour will also differ if $string ends in newline characters as command substitution strips them.

0
1

with GNU sed : (as you wanted in sed)

$ sed -E 's/\$([A-Z]+([A-Z]*_)*[A-Z]+)\$/@\L\1@/g;s/@([a-z]+)_/@\1./g;s/_([a-z]+)@/\.\1@/g; :X s/(\.[a-z]+)_/\1\./g; tX'

Output :

$ echo 'Lorem $IPSUM$ $dolor_sit_amet _ _ _ consectetur $ADIPISICING_ELIT$ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ $ET_DOLORE_MAGNA_ALIQUA$' | sed -E 's/\$([A-Z]+([A-Z]*_)*[A-Z]+)\$/@\L\1@/g;s/@([a-z]+)_/@\1./g;s/_([a-z]+)@/\.\1@/g; :X s/(\.[a-z]+)_/\1\./g; tX'
Lorem @ipsum@ $dolor_sit_amet _ _ _ consectetur @adipisicing.elit@ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ @et.dolore.magna.aliqua@
0
1

I think a sed solution would be too messy. May I suggest awk?

dollars.awk:

{
    for(i=1;i<=NF;i++){
        if($i ~ /\$[[:upper:]_]+\$/){    #If field contains two $
            gsub(/\$/,"@",$i) #Substitute $ with @
            gsub(/_/,".",$i)  #Substitute _ with .
            $i=tolower($i)    #Make field lowercase
        }
    }
}
1
$ awk -f dollars.awk file
Lorem @ipsum@ $dolor_sit_amet _ _ _ consectetur @adipisicing.elit@ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ @et.dolore.magna.aliqua@
1
  • Thanks for your reply. This does look much cleaner. I'll see if I can use this solution.
    – svz
    Jun 25, 2020 at 13:41
1

Another, slightly shorter GNU sed

sed -E "s/\\\$([A-Z_]+)\\\$/@\L\1@/g; :X s/(@[a-z.]+)_/\1./; tX" file

The assumption is that @ doesn't occur naturally in your source and within the $ was only originally [A-Z_]

Lorem $IPSUM$ $dolor_sit_amet _ _ _ consectetur $ADIPISICING_ELIT$ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ $ET_DOLORE_MAGNA_ALIQUA$
Lorem @ipsum@ $dolor_sit_amet _ _ _ consectetur @adipisicing.elit@ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ @et.dolore.magna.aliqua@

This is fine with your sample but will fail if you have $BLAH_BLAH$blah_ -> @blah.blah@blah. instead of @blah.blah@blah_

Edit re @Quasimodo comment + -> * in the second sed

sed -E "s/\\\$([A-Z_]+)\\\$/@\L\1@/g; :X s/(@[a-z.]*)_/\1./; tX" file

Lorem $_IPSUM$ $dolor_sit_amet _ _ _ consectetur $ADIPISICING_ELIT$ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ $ET_DOLORE_MAGNA_ALIQUA$
Lorem @.ipsum@ $dolor_sit_amet _ _ _ consectetur @adipisicing.elit@ sed do $EUISMOD_TEMPOR INCIDIDUNT_UT_LABORE$ @et.dolore.magna.aliqua@
0
1

Perl with it's Eval-d rhs of the s/// command is tailor made for ths pbm:

echo "$var" |\
perl -lpe 's/(\$[A-Z_]+\$)/$1 =~ tr|$A-Z_|@a-z.|r/ge' 

GNU sed can also be used.

sed -Ee '
  :a; s/\$([A-Z_]+)\$/\n\L\1\n/; ta
  :b; s/(\n[^\n]*)_/\1./; tb
  y/\n/@/
 ' 

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .