As an example, say I have:

One, Two.
OnE, Two.

And I want to replace only the lines with all upper-case with lower case so that I get:

One, Two.
OnE, Two.
one, two.

I am trying:

gsed '/[a-z]/!c\L&'

It's matching the lines correctly, but replacing them with: L&

One, Two.
OnE, Two.

How do I get it convert to lower case?

I tried 2 or 3 backslashes before the L but it just puts them in the output.

I will use awk or tr or some other utility if there's a better choice.

Also, if the version matters:

gsed --version
gsed (GNU sed) 4.9
  • Do you need to handle ÜNICØDÉ ? (thx @Philippos for this text!). Apr 29 at 18:50

8 Answers 8


Using any POSIX awk:

$ awk '!/[[:lower:]]/ { $0=tolower($0) } 1' file
One, Two.
OnE, Two.
one, two.

I think you need to use s/// for & to work, e.g.:

$ sed -e '/[a-z]/!s/.*/\L&/' < test.txt
One, Two.
OnE, Two.
one, two.

You could do the same or similar with Perl (which might be available where GNU sed isn't):

% perl -pe 's/.*/\L$&/ if not /[[:lower:]]/' < test.txt
One, Two.
OnE, Two.
one, two.


% perl -pe '$_ = lc if not /[[:lower:]]/' < test.txt
One, Two.
OnE, Two.
one, two.
  • For standard sed, the y command is the tool of choice. Unfortunally, it doesn't accept character collections like [A-Z]. Thus, you need some support by the shell: sed "/[a-z]/! y/$(printf %s {A..Z} / {a..z})/"
    – Philippos
    Apr 24 at 10:18
  • 1
    @Philippos, and then you get to the issue that brace expansion isn't a standard feature, so you'd need to just spell out the list ;)
    – ilkkachu
    Apr 24 at 10:19

With Perl's ternary operator:

$ perl -ne 'print /[a-z]/ ? $_ : lc' file
One, Two.
OnE, Two.
one, two.


Operator Meaning
perl executable
-n parse current file content as if we use while (<>) ...
-e Perl's expression
print print
/regex/ ? : ternary operator, based on the matching of boolean result of the regex
/[a-z]/ if there's a lower case matching...
$_ ...then print current default variable of current line $_
lc ...else print the current line in lower case (implicit use of $_)

I would do this in perl:

$ perl -pe '$_ = lc() if /^[A-Z., ]+$/' file 
One, Two.
OnE, Two.
one, two.

The options are -e to pass a script, and -p to print every line of the input after applying the script given by -e to it. The script itself uses the special variable $_ which means "the current line" and it converts it to lowercase (lc prints its input string in lower case, and by default, it acts on $_) only if the current line contains nothing but capital letters, spaces, commas and full stops (/^[A-Z., ]+$/).

If you go for a perl approach, use Ilkkachu's instead of mine since his doesn't depend on defining acceptable characters, and instead works by looking for lower case ones specifically.


Here's my (short and sweet) Perl solution:

perl -wpe '$_ = lc unless m/[a-z]/' file.txt

High-level explanation: This is simply saying: Before printing out each line in file file.txt, convert the line to lowercase unless it contains any lower-case letters.

You can also replace m/[a-z]/ with tr/a-z// so that it looks like this:

perl -wpe '$_ = lc unless tr/a-z//' file.txt

The difference between them is that m/[a-z]/ is meant to be evaluated as a boolean (that is, whether or not it contains the characters a thru z), whereas tr/a-z// returns the number of characters in the range of a thru z. (If it returns a number greater than zero, then the lower-casing (that is, $_ = lc) will not happen.)

Which one is better? I haven't benchmarked them, but the difference is probably so small that it's better to just use whichever one you're more comfortable with.

But remember: The m/[a-z]/ approach uses the [ and ] square brackets, whereas the tr/a-z// approach does not, but ends with two /s in a row. It may be a bit much to remember both, so I recommend using whichever one is easier for you to remember.


Using Raku (formerly known as Perl_6)

~$ raku -ne 'if / <:Ll> / { $_.put } else { $_.lc.put };'  file

#OR (more simply)

~$ raku -ne 'put / <:Ll> / ?? $_ !! $_.lc;'  file

The first two answers below use Raku's -ne non-autoprinting command line switches. The regex character class <:Ll> stands for "Unicode Letter lower". The first answer above is obviously an if/else construct. The second answer uses Raku's ternary operator ( < test > ?? True !! False ).


~$ raku -e 'for lines() { when / <:Ll> / {.put}; default {$_.lc.put} };'  file 


~$ raku -pe '$_ .= lc if not / <:Ll> /;'  file

The third answer (first line in code block immediately above) uses Raku's "case" statement construction: when and default are used to satisfy the substitution criteria.

The fourth answer (second line in code block immediately above) uses Raku's -pe autoprinting (sed-like) command line flags, in conjunction with Raku's .= mutating-assignment operator.

Sample Input:

One, Two.
OnE, Two.


Sample Output (all code examples):

One, Two.
OnE, Two.
one, two.

русский язык russian
νέα ελληνικά greek
"heiss heiß" german (word)

The Raku answers handle Unicode (tested briefly above). For example, in the last line note the uppercase German (U+1E9E) is successfully converted to lowercase German ß (U+00DF). (This lettercasing was adopted into Unicode in 2008 and according to Wikipedia, "The capital letter was finally adopted as an option in standard German orthography in 2017.").

https://docs.raku.org/language/control#when https://en.wikipedia.org/wiki/ß#Development_of_a_capital_form


You can do it in Python as follows:

Create a Python script

 % cat all_upper_to_lower.py 
import sys

data = sys.stdin.readlines()
for line in data:
    line = line.rstrip('\n')
    new_line = line.lower() if line.isupper() else line

Create some sample data

 % cat input.txt 
One, Two.
OnE, Two.

Run the script

 % cat input.txt | python all_upper_to_lower.py 
One, Two.
OnE, Two.
one, two.

Here is a ruby:

ruby -lpe ' $_=$_.downcase unless /[[:lower:]]/' file 


One, Two.
OnE, Two.
one, two.

How this works:

  1. Command line switches -lpe do the following:

    -l chomp the line ending;

    -p Put a while gets(); ... end for sed like processing;

    -e Execute the script in the string following

  2. $_=$_.downcase make the line of input lowercase before printing

  3. unless /[[:lower:]]/ If there is any lowercase, do not call downcase. You could also negate and do if !/[[:lower:]]/

And you can do something similar in Perl:

perl -lpE '$_=lc $_ unless /[[:lower:]]/' file 
# same

You must log in to answer this question.

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