64

Is there any implementation of regex that allow to replace group in regex with lowercase version of it?

SW4
  • 69,876
  • 20
  • 132
  • 137

6 Answers6

63

If your regex version supports it, you can use \L, like so in a POSIX shell:

sed -r 's/(^.*)/\L\1/'
Kim
  • 1,412
  • 1
  • 11
  • 10
  • 3
    This appears to work in a surprising number of regex kits, even if you're not in a shell-like environment. – Tim Jun 26 '13 at 22:28
  • 3
    @Tim: Agreed, even Notepad++ supports it. – Nelson Rothermel Aug 15 '13 at 18:21
  • 2
    This does not work in `zsh` or `bash` on OSX (yes I'm using `-E` instead of `-r`). – dcow Dec 21 '14 at 19:13
  • BSD sed is too limited. On OS X use Homebrew and install gnu-sed instead. Then it works. – Kim Dec 22 '14 at 09:19
  • 1
    Works in Linux's `rename` command, which at least in Ubuntu 14 is a perl script (/usr/bin/prename). – Camille Goudeseune Jun 15 '15 at 15:45
  • Since `*` is greedy and `.` stands for any character, you don't have to anchor the pattern with `^`. With gnu sed, you can also remove the capture group and the -r switch and write: `sed 's/.*/\L&/'`. On the other hand writing `sed --posix 's/\(.*\)/\L\1/'` doesn't work with gnu sed (and add a L at the start of the line). – Casimir et Hippolyte Oct 28 '17 at 19:01
  • Just want to add that the \L works in VS Code as well. My replace regex is `$1\L$2` which keeps group 1 as it is, but change group 2 to lowercase. – ABVincita Mar 29 '22 at 06:02
41

In Perl, you can do:

$string =~ s/(some_regex)/lc($1)/ge;

The /e option causes the replacement expression to be interpreted as Perl code to be evaluated, whose return value is used as the final replacement value. lc($x) returns the lowercased version of $x. (Not sure but I assume lc() will handle international characters correctly in recent Perl versions.)

/g means match globally. Omit the g if you only want a single replacement.

j_random_hacker
  • 50,331
  • 10
  • 105
  • 169
22

If you're using an editor like SublimeText or TextMate1, there's a good chance you may use

\L$1

as your replacement, where $1 refers to something from the regular expression that you put parentheses around. For example2, here's something I used to downcase field names in some SQL, getting everything to the right of the 'as' at the end of any given line. First the "find" regular expression:

(as|AS) ([A-Za-z_]+)\s*,$

and then the replacement expression:

$1 '\L$2',

If you use Vim (or presumably gvim), then you'll want to use \L\1 instead of \L$1, but there's another wrinkle that you'll need to be aware of: Vim reverses the syntax between literal parenthesis characters and escaped parenthesis characters. So to designate a part of the regular expression to be included in the replacement ("captured"), you'll use \( at the beginning and \) at the end. Think of \ as—instead of escaping a special character to make it a literal—marking the beginning of a special character (as with \s, \w, \b and so forth). So it may seem odd if you're not used to it, but it is actually perfectly logical if you think of it in the Vim way.


1 I've tested this in both TextMate and SublimeText and it works as-is, but some editors use \1 instead of $1. Try both and see which your editor uses.

2 I just pulled this regex out of my history. I always tweak regexen while using them, and I can't promise this the final version, so I'm not suggesting it's fit for the purpose described, and especially not with SQL formatted differently from the SQL I was working on, just that it's a specific example of downcasing in regular expressions. YMMV. UAYOR.

iconoclast
  • 21,213
  • 15
  • 102
  • 138
15

Several answers have noted the use of \L. However, \E is also worth knowing about if you use \L.

\L converts everything up to the next \U or \E to lowercase. ... \E turns off case conversion.

(Source: https://www.regular-expressions.info/replacecase.html )

So, suppose you wanted to use rename to lowercase part of some file names like this:

artist_-_album_-_Song_Title_to_be_Lowercased_-_MultiCaseHash.m4a
artist_-_album_-_Another_Song_Title_to_be_Lowercased_-_MultiCaseHash.m4a

you could do something like:

rename -v 's/^(.*_-_)(.*)(_-_.*.m4a)/$1\L$2\E$3/g' *
3

In Perl, there's

$string =~ tr/[A-Z]/[a-z]/;
Hank Gay
  • 70,339
  • 36
  • 160
  • 222
2

Most Regex implementations allow you to pass a callback function when doing a replace, hence you can simply return a lowercase version of the match from the callback.

AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306