118

I have temp file with some lower-case and upper-case contents.

Input

Contents of my temp file:

hi
Jigar
GANDHI
jiga

I want to convert all upper to lower.

Command

I tried the following command:

sed -e "s/[A-Z]/[a-z]/g" temp

but got wrong output.

Output

I want it as:

hi
jigar
gandhi
jiga

What needs to be in the substitute part of argument for sed?

1

7 Answers 7

171

If your input only contains ASCII characters, you could use tr like:

tr A-Z a-z < input 

or (less easy to remember and type IMO; but not limited to ASCII latin letters, though in some implementations including GNU tr, still limited to single-byte characters, so in UTF-8 locales, still limited to ASCII letters):

tr '[:upper:]' '[:lower:]' < input

if you have to use sed:

sed 's/.*/\L&/g' < input

(here assuming the GNU implementation).

With POSIX sed, you'd need to specify all the transliterations and then you can choose which letters you want to convert:

sed 'y/AǼBCΓDEFGH.../aǽbcγdefgh.../' < input

With awk:

awk '{print tolower($0)}' < input
11
  • 4
    Please note that \L is a GNU extension.
    – Anthon
    Dec 5, 2014 at 7:48
  • \L works good for me so far. En light the point that you are trying to make GNU extension Dec 5, 2014 at 7:56
  • 2
    @JigarGandhi. sed is a Unix command. Different systems have different variants with different behaviour and functionality. Thankfully, nowadays, there's a standard that most conform to so you can count on a minimum set of features common to all. \L is not among them and was introduced by GNU sed (matches the same operator in standard ex/vi) and is generally not available in other implementations. Dec 5, 2014 at 13:25
  • 11
    Note that some tr implementations like GNU tr don't work properly in multi-byte locales (most of them are nowadays, try echo STÉPHANE | tr '[:upper:]' '[:lower:]' for instance). On GNU systems, you may prefer the sed variant or awk's tolower(). Dec 5, 2014 at 13:29
  • 5
    Slight correction: sed 's/.*/\L&/g' < input. The \1 reference to the matched substring won't work unless you specify the substring with parenthesis as wurtle does in his. However, it's slightly cleaner to use & to represent the whole match, as shown Nov 4, 2016 at 15:30
34

Using vim, it's super simple:

$ vim filename
gg0guGZZ

Opens the file, gg goes to the first line, 0, first column. With guG, lowers the case of all the characters until the bottom of the file. ZZ saves and exits.

It should handle just about anything you throw at it; it'll ignore numbers, it'll handle non ASCII.

If you wanted to do the opposite, turn the lower cased letters into upper case, swap the u out for a U: gg0gUGZZ and you're set.

4
  • 22
    Lol "super simple"
    – blambert
    Feb 17, 2017 at 21:50
  • this obviously doesn't scale well for many files Nov 25, 2017 at 13:40
  • 1
    @CoreyGoldberg vim file1 file2 fileetc and then something like :bufdo gg0guG:w<CR> would probably work for any number of files. Have not tested that though! Apr 22, 2018 at 0:18
  • @TankorSmash that still doesn't scale to a large number of files Apr 22, 2018 at 15:39
19

I like dd for this, myself.

<<\IN LC_ALL=C 2<>/dev/null \
dd conv=lcase
hi
Jigar 
GANDHI
jiga
IN

...gets...

hi
jigar
ghandi
jiga

The LC_ALL=C is to protect any multibytes in input - though any multibyte capitals will not be converted. The same is true for (GNU) tr - both apps are prone to input mangling in any non-C locale. iconv can be combined with either for a comprehensive solution.

The 2>/dev/null redirect discards dd's default status report - and its stderr. Without it dd would follow completion of a job like the above w/ printing information like how many bytes were processed and etc.

1
  • This solution is way faster than tr when handling large files, thanks! Aug 18, 2016 at 13:02
15

You can also use Perl 5:

perl -pe '$_=lc' temp

The option -p tells perl to run the specified expression once for each line of input, printing the result, i.e. the final value of $_. -e indicates that the program will be the next argument, as opposed to a file containing the script. lc converts to lowercase. Without an argument, it will operate on $_. And $_= saves that again so it will get printed.

A variation of that would be

perl -ne 'print lc' temp

Using -n is like -p except that $_ won't get printed in the end. So instead of saving to that variable, I'm including an explicit print statement.

One benefit of Perl in contrast to sed is that you don't need any GNU extensions. There are projects which have to be compatible with non-GNU environments but which also already have Perl a s a dependency. Compared with tr, it might be that Perl lc can be more easily made locale-aware. See the perllocale man page for details.

9

You need to capture the matched pattern and then use it in the replacement with a modifier:

sed 's/\([A-Z]\)/\L\1/g' temp

The \(...\) "captures" the enclosing matched text, the first capture goes to \1, the next to \2, etc. The numbering is according to opening brackets in case of nested captures.

The \L converts the captured pattern to lower case, there's also \U for upper case.

2
  • 3
    you dont need to do this - the whole pattern is always caught in &
    – mikeserv
    Dec 5, 2014 at 9:41
  • True, but then I would have missed the opportunity to explain capturing matches :-)
    – wurtel
    Dec 5, 2014 at 12:47
2

Further to MvG's answer, you could also use Perl 6:

perl6 -pe .=lc temp

Here $_ is implicit, and you don't need the single quotes to protect it from expansion by the shell ($_ being a special Bash parameter; see: https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html)

1
  • The www.gnu.org link you provide does not list Perl6's (now Raku's) $_ "topic" variable as being used by Bash, at least not as a "Bash: Special Parameter". Nov 12, 2021 at 7:57
1

Using Emacs, you could first select all text in your buffer. Then invoke

M-x downcase-region

You must log in to answer this question.

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