4

According to https://caseconverter.com/

“Upper Case” WHICH CONVERTS ALL THE LETTER INTO CAPITALS LIKE THIS.

“Lower Case” which converts all the letters into small letters like this.

“Proper Case” Which Converts The Text So Every Word Has Its First Letter Capitalised Like This

“Sentence Case”. This capitalises the first letter of each sentence, and converts the rest of the text to lower case. So the first letter after every full stop is automatically converted into a capital letter.

The first two can be accomplish easily with tr command.

user@linux:~$ tr [:lower:] [:upper:] <<< eXaMPLe
EXAMPLE
user@linux:~$ 

user@linux:~$ tr [:upper:] [:lower:] <<< eXaMPLe
example
user@linux:~$ 

or

user@linux:~$ tr [a-z] [A-Z] <<< eXaMPLe
EXAMPLE
user@linux:~$ 

user@linux:~$ tr [A-Z] [a-z] <<< eXaMPLe
example
user@linux:~$ 

What about the last two which are "Proper Case" and "Sentence Case"?

Is it possible?

If yes, please let me know.

If not, what is the alternative?

3 Answers 3

3

Note that unless your shell is fish which doesn't support the [...] globbing operator, you should quote those [:lower:], [A-Z], otherwise they could be expanded by the shell to the list of matching files in the current directory (or report an error if there's no match):

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

Other notes:

  • the GNU implementation of tr only supports single byte characters, so in a UTF-8 locale, it will only capitalise English letters without diacritics.
  • tr '[A-Z]' '[a-z]' is fine, but you can also simply do tr A-Z a-z (in POSIX compliant implementations). However, it's only guaranteed to match only on ABCDEFGHIJKLMNOPQRSTUVWXYZ in the C/POSIX locale.

To Capitalise the first letter of every word, with the GNU implementation of sed, you can do:

sed -E "s/[[:alnum:]_'-]+/\u&/g"

We're including ', - and _ but no other punctuation characters so as to turn foo-bar2baz,foo into Foo-bar2baz,Foo.

That works with multi-byte characters, but generally not with combining characters as most locales won't consider them as alnum:

$ echo $'ste\u0301phane' | sed -E "s/[[:alnum:]_']+/\u&/g"
StéPhane

To consider those, you could switch to perl, where those can be matched with \pM:

$ echo $'ste\u0301phane chazelas' | perl -Mopen=locale -pe 's/[\w\pM'\''-]+/\u$&/g'
Stéphane Chazelas

For the sentence capitalisation, it's quite tricky, you have to capitalise the first letter found at the start of the text or after a sentence delimiter (like ., ?, …) or sentence introducer (¿, ¡), allowing any number of whitespace in between, but also things like (, [, ", , , «…). Depending on which language(s) you want to support, you may want to consider more.

You could do it with perl with something like:

perl -0777 -C -pe 's/(^|[.!?…⁇⁈⁉¿¡])[\s([{"`‶‷«]*\K\p{lL}/\u$&/g'

Here assuming a UTF-8 locale and input and only covering a few of those cases.

In any case, that's not something that can be done with tr alone as tr transliterate every character, it can't be told to transliterate only some.

1

No, it's not possible to do with tr alone. tr has no understanding of words or sentences required to make this work.

As an alternative, you can try Perl with its powerful pattern engine:

$ perl -pe 's/\b[\p{L}\p{Pd}]+/ucfirst lc $&/ge' <<< 'A HEART-SHAPED BOX'
A Heart-shaped Box
$ perl -pe 's/\p{L}.*?[.?!]/ucfirst lc $&/ge' <<< 'for sale. baby shoes. never worn.'
For sale. Baby shoes. Never worn.

The above, however, is a quick-and-dirty solution and doesn't cover every possible case and tweak one might want to apply here (non-Latin alphabets and non-ASCII Latin letters, locale-dependent case, articles not being capitalised, proper nouns in sentences being capitalised, etc.). Then again, neither can tr do it.

1

tr only changes single characters to other single characters (or deletes them), and it has sense of the context of any character. It can't therefore distinguish between a character at the beginning, middle, or end of a word. It does not even know what a "word" is.

Changing a text to title case (what you call "proper case") or to sentence case is impossible using tr.

You need a tool that you can use to give some context to the individual character with.

This is a naive GNU sed program that converts a text to title case by matching separate words and changing their first character:

$ sed 's/\<\([[:lower:]]\)\([[:alnum:]]*\)/\u\1\2/g' file
There Is No Danger On The Roof. There Is No Cow On The Ice.

The \< matches at transition point between a non-word character and a word character (i.e., at the start of a word). The rest of the regular expression matches a lower case letter followed by any number of alphanumeric characters. If it matches, it changes the lower case letter to an upper case letter and appends the rest of the word. The upper-casing of the first letter is using a GNU sed extension (this would not work most other sed implementations).

For sentence casing a text, another naive GNU sed variation:

$ sed 's/\<\([[:lower:]]\)\([^[:punct:]]*\)/\u\1\2/g' file
There is no danger on the roof. There is no cow on the ice.

This is more or less the same thing again, but instead of matching across a word of alphanumerical characters, we match across a string of characters that are not punctuation characters.

Note that this only works on really simple texts of the type that you show in the question. The second sed, would, for example, not properly cope with the question what's that?, due to ' being a punctuation character matched by [[:punct:]].

Your Answer

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