21

My question is how can I convert all text from uppercase to lowercase and vice versa? That is to change the cases of all the letters. It has to be done with a sed replacement somehow.

MEZesUBI
  • 389
  • 1
  • 2
  • 9

7 Answers7

25

Here is a straight way in sed:

$ echo qWeRtY | sed -e 'y/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/'
QwErTy

or a shorter way with GNU sed, working with any character for which a lowercase<->uppercase conversion exists in your locale:

$ echo qWeRtY | sed -E 's/([[:lower:]])|([[:upper:]])/\U\1\L\2/g'
QwErTy

if you can use another tools, like:

perl (limited to ASCII letters):

$ echo qWeRtY | perl -pe 'y/[a-z][A-Z]/[A-Z][a-z]/'
QwErTy

perl (more generally):

$ echo 'αΒγ' | perl -Mopen=locale -pe 's/(\p{Ll})|(\p{Lu})/uc($1).lc($2)/ge'
ΑβΓ
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
cuonglm
  • 150,973
  • 38
  • 327
  • 406
  • 3
    Your second one assumes a GNU `sed` and alternate case in the input. Use `sed -re 's/([[:lower:]]?)([[:upper:]]?)/\U\1\L\2/g'` instead (still GNU specific). The first one only converts the 26 ASCII latin letters, while the second converts any letter recognised as such by your locale. The `tr` one only makes sense in ASCII locales. The `perl` one only works for ASCII latin letters. – Stéphane Chazelas Jun 08 '14 at 20:47
17

POSIXly, that can't be done with sed except by providing the complete set of letters you want to transliterate as @cuonglm has shown.

It could be done with tr though, and that's what tr is for (transliterate):

tr '[:lower:][:upper:]' '[:upper:][:lower:]'

However, on Linux, it's got limitations. Of the 3 tr implementations commonly found on Linux-based systems:

  • with GNU tr, that only works for single-byte character sets. For instance, on Stéphane Chazelas in UTF-8 locales, that gives sTéPHANE cHAZELAS instead of sTÉPHANE cHAZELAS. That's a known limitation of GNU tr.
  • with tr from the heirloom toolchest, that doesn't work (you get stéphane chazelas).
  • That's not the kind of thing busybox tr will do.

On FreeBSD that works OK though. You'd expect it to work OK in certified Unix systems as well.


The bash shell has a dedicated operator for that:

in=AbCdE
out=${in~~}

With zsh -o extendedglob:

out=${in//(#b)(([[:lower:]])|([[:upper:]]))/${(U)match[2]}${(L)match[3]}}
Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • So in desktop world only OSX does it? Why can't it work? Is it just the different implementations as it seems there is a constant offset in the hex value between the lower case version of the accented char and it's uppercase counterpart? –  Jun 08 '14 at 23:31
  • 1
    @illuminÉ, not sure what you mean by _desktop world_. AFAICS, the problem is with GNU, most Unices have "desktops". Apart from ASCII and some iso8859 charsets, I'm not aware that you can generalise the hex offset thing, and that wouldn't make sense with encodings like UTF-8. For instance in UTF-8, uppercase `ⴠ` (e2 b4 a0) is `Ⴠ` (e1 83 80); both `i` (69) and `ı` (c4 b1) have `I` (49) as uppercase (except in Turkish locales where `i` becomes `İ`). The reason it doesn't work with GNU `tr` is that GNU `tr` works with bytes and not characters. – Stéphane Chazelas Jun 09 '14 at 05:34
  • I sort of meant mainstream but it doesn't make sense really so thanks for the heads up. I just looked at the French accented chars(and really just "é") and made very simplistic assumptions, forgetting again that it's about bytes. But the heirloom one? I'll go read that answer again! –  Jun 09 '14 at 05:43
  • 1
    @illuminÉ, for heirloom, it's a different issue, it seems it only supports only one occurrence of `[:lower:]` or `[:upper:]` (so the first one is ignored). Even in French, `œ -> Œ` is `c5 93 -> c5 92` in UTF-8 and `bd -> bc` in iso8859-15. – Stéphane Chazelas Jun 09 '14 at 05:49
3

If your main objective is to convert a file from lowerclass to upperclass, why dont you use tr and STDOUT to convert your file:

$cat FILENAME | tr a-z A-Z > FILENAME2

Where FILENAME is your original file. Where FILENAME2 is your converted output file.

Prvt_Yadav
  • 5,732
  • 7
  • 32
  • 48
Rick
  • 31
  • 1
2

Though this has the same limitations already mentioned as the tr solution offered by Stéphane Chazelas, it is another way to do it:

{   echo QWERTYqwerty | dd conv=lcase
    echo QWERTYqwerty | dd conv=ucase 
} 2>/dev/null

OUTPUT

qwertyqwerty
QWERTYQWERTY

I dump stderr into /dev/null there because dd also provides statistics of all its operations on the 2 file descriptor. This can be useful depending on what you're doing, but wasn't for this demonstration. All of the other stuff you can do with dd still applies, for instance:

echo QWERTYqwerty | dd bs=1 cbs=6 conv=unblock,ucase 2>/dev/null

OUTPUT:

QWERTY
QWERTY
mikeserv
  • 57,448
  • 9
  • 113
  • 229
1

using awk:

awk '{print tolower($0)}' file.txt | tee file.txt
Hackaholic
  • 1,951
  • 1
  • 10
  • 10
1

ruby has a string method for that, similar usage from command line like perl

$ echo 'qWeRtY' | ruby -pe '$_.swapcase!'
QwErTy

See also ruby-doc Encoding

$ ruby -e 'puts Encoding.default_external'
UTF-8
$ echo 'αΒγ'  | ruby -pe '$_.swapcase!'
ΑβΓ
Sundeep
  • 11,753
  • 2
  • 26
  • 57
-1

Keep simple thing simple. The filter designed to translate characters is tr.

echo 1ude1UDE | tr [:upper:][:lower:] [:lower:][:upper:]
  • 2
    That's a broken (because of the missing quotes around the globbing operators) version of [an answer already given 2 years earlier](/a/136132) – Stéphane Chazelas Aug 25 '17 at 12:23