2

I have the following list of items:

bigBone
fishMarket
dogCollar
...

I need to generate a two character mapping like this:

bigBone -> bb
fishMarket -> fm
dogCollar -> dc
...

How can I create the above mapping using sed?

I have tried something like:

sed -i -r 's/^([a-z]{1})[a-z]+([A-Z]{1})[a-zA-Z]+/ -> \1\L\2/' file

I saw this question, but not sure how to incorporate the concept here. Thanks.

1
  • See the comment of Stephane Chazelas to my answer. May 6, 2014 at 9:11

6 Answers 6

4

If I understand you correctly then you want to keep the whole line and just append something:

sed -r 's/^([a-z]{1})([a-z]+)([A-Z]{1})([a-zA-Z]+)$/\1\2\3\4 -> \1\L\3/' file

edit:

devnull had to remind me of it that there is an easy solution to this:

sed -r 's/^([a-z]{1})[a-z]+([A-Z]{1})[a-zA-Z]+/& -> \1\L\2/' file

Or, a bit more elegant (than my first try):

sed -r '
h
s/^([a-z]{1})[a-z]+([A-Z]{1})[a-zA-Z]+/ -> \1\L\2/
t append
b
: append
H
g
s/\n//' file
2
  • @QuestionOverflow It is even simpler, see the edit. May 6, 2014 at 8:39
  • 2
    ranges like [a-z] only make sense in the POSIX or C locales. So you'd want either LC_ALL=C sed ... or to use [[:lower:]] instead. Note that {1} is redundant. May 6, 2014 at 9:05
4

Using GNU sed:

sed -r 's/(.)[^[:upper:]]*(.).*/& -> \1\L\2/' inputfile

For your input, it'd produce:

bigBone -> bb
fishMarket -> fm
dogCollar -> dc
0
3

To generalise to fooBarBaz -> fbb, abCdEfGh -> aceg, with GNU sed:

sed -r 's/(.)(.*)/\1\n\2 -> \L\1/;:1
        s/\n([^[:upper:]]*([[:upper:]]))(.*)/\1\n\3\L\2/;t1;s/\n//'

POSIX sed doesn't have \L. So portably, you'd have to resort to using y and manually enter all the characters you want converted to lowercase. Something like:

LC_ALL=C sed '/^\([[:alpha:]]\).*/{
h;s//\1/;y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/;G
s/\(.\).\(.\)\(.*\)/\2\
\3 -> \1/;:1
/.*\n[^A-Z]*\([A-Z]\).*/{h;s//\1/
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/;G
s/^\(.\)\n\(.*\)\n\([^A-Z]*[A-Z]\)\(.*\)/\2\3\
\4\1/;t1
}
s/\n//;}'
2

Here it is with a POSIX compliant script. I wasn't aware that POSIX sed leaves the behavior of s///[n]g unspecified, but, sure enough, it is not a well-defined behavior. Anyway, it's easy to handle without it - I just don't like using too many back-references if it can be helped, usually.

sed '/^[a-z]*[A-Z].*$/{ h
    s/\(.\)[^A-Z]*/\1/g
    y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
    H ; g 
    s/\n/ -> /
    }' <<\DATA
bigBone
fishMarket
dogCollar
DATA

#OUTPUT
bigBone -> bb
fishMarket -> fm
dogCollar -> dc
12
  • 1
    Have you tried that? Doesn't work here (upper case vs. lower case)). May 6, 2014 at 7:44
  • 1
    @HaukeLaging - I just needed to fix y/ - it would have worked as it was if I had used -r but there's no need whatsoever to write a non-portable script for such a simple application. If it was you who downvoted this post and you did so due to the case conversion, that issue is resolved, and it is now faster and more portable than your own script. I believe you should reverse that vote.
    – mikeserv
    May 6, 2014 at 7:57
  • @HaukeLaging - even better now. Thank you, Hauke. I expect that was you.
    – mikeserv
    May 6, 2014 at 8:18
  • 1
    \L is GNU specific anyway, so you might as well use -r May 6, 2014 at 9:24
  • 1
    Seriously, @StephaneChezales? OR\| isnt portable? I got both of those from the Grymoire. And its pretty old, too. I think hes even got a letter or two from you in that manual of his in there somewhere... I thought it was portable code. I can easily do it without it. Im just... Kinda done with it... Oh well, ill fix it. By the way - have you got the sed specs memorized or something? I couldnt remember all of those ins and outs like that. Youre definitely one of a kind.
    – mikeserv
    May 6, 2014 at 16:53
1

This answer is just similar to @devnull's,

$ sed 's/\(.\).*\([A-Z]\).*/& -> \1\L\2/g' file
bigBone -> bb
fishMarket -> fm
dogCollar -> dc
1

If you can use perl, try:

$ perl -MList::Util=first -F// -aple '$_ .= " -> ".$F[0].lc(first{ord() < 97} @F)' file
bigBone -> bb
fishMarket -> fm
dogCollar -> dc

You must log in to answer this question.

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