17

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.

  • 4
    tr would be more suitable than sed. – choroba Jun 8 '14 at 19:25
20

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'
ΑβΓ
  • 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 8 '14 at 20:47
16

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]}}
  • 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? – user44370 Jun 8 '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 9 '14 at 5: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! – user44370 Jun 9 '14 at 5: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 9 '14 at 5:49
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
  • It doesn't swap the case though (as in aBc is not converted to AbC). – Stéphane Chazelas Jun 9 '14 at 5:24
  • 1
    @StéphaneChazelas - true, but unless I've misunderstood, that wasn't the question, was it? – mikeserv Jun 9 '14 at 6:32
2

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.

1

using awk:

awk '{print tolower($0)}' file.txt | tee file.txt
  • you sure this is going to work? >file.txt would start off by truncating the file – iruvar Sep 22 '16 at 18:04
  • 2
    Then you've obviously not tried it. – Stéphane Chazelas Oct 21 '16 at 11:42
0

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!'
ΑβΓ
-1

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

echo 1ude1UDE | tr [:upper:][:lower:] [:lower:][:upper:]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.