197

I searched SO and found that to uppercase a string following would work

str="Some string" echo ${str^^} 

But I tried to do a similar thing on a command-line argument, which gave me the following error

Tried

#!/bin/bash ## Output echo ${1^^} ## line 3: ${1^^}: bad substitution echo {$1^^} ## No error, but output was still smaller case i.e. no effect 

How could we do this?

2

6 Answers 6

205

The syntax str^^ which you are trying is available from Bash 4.0 and above. Perhaps yours is an older version (or you ran the script with sh explicitly):

Try this:

str="Some string" printf '%s\n' "$str" | awk '{ print toupper($0) }' 
7
  • 1
    From mtk's words I understood that case modification actually works for him with variables. Commented Oct 16, 2012 at 12:46
  • 4
    You are correct. I checked the version, its 3.2.25. Your solution works and also I tried tr '[a-z]' [[A-Z]'. Commented Oct 16, 2012 at 12:51
  • 27
    @mtk That should be tr '[a-z]' '[A-Z]'. Commented Oct 17, 2012 at 9:43
  • 3
    I'm running GNU bash, version 4.3.42(1)-release (x86_64-apple-darwin14.5.0), and I get the same error as OP, so I don't think this is available on any bash 4.0 and above as you say. Commented Jan 26, 2016 at 23:19
  • 2
    @ZoeyHewll – printf doesn't accept options, so it's much more reliable and predictable given something like str="-n" (try echo "-n" or echo -- "-n" vs printf "%s\n" "-n"). Commented Oct 12, 2021 at 20:12
170
echo "lowercase" | tr a-z A-Z 

Output:

LOWERCASE 
4
  • 5
    I think POSIX does not require the / as in tr /a-z/ /A-Z/ before my edit: this just works because it replaces / by / but is useless: pubs.opengroup.org/onlinepubs/9699919799/utilities/tr.html There also exists the more obscure and less useful tr '[:lower:]' '[:upper:]'. Commented May 9, 2018 at 16:22
  • Correct. tr is very different, but it works. Commented Sep 6, 2018 at 16:52
  • 1
    umlauts are not working with this. Commented Jan 31, 2019 at 15:04
  • 1
    As @CiroSantilli冠状病毒审查六四事件法轮功 mentioned, you can use `tr '[:lower:]' '[:upper:]' to handle non-a-z characters. (Indeed, the "less useful" comment seems to be the opposite. using lower/upper covers a wider range of characters.) Commented Apr 8, 2020 at 13:30
38

Be careful with tr unless A-Z is all you use. For other locales even '[:lower:]' '[:upper:]' fails, only awk's toupper and bash (v4+) works:

$ str="abcåäö" $ echo "$str" | tr '/a-z/' '/A-Z/' ABCåäö $ echo "$str" | LC_ALL=sv_SE tr '[:lower:]' '[:upper:]' ABCåäö $ echo "$str" | awk '{print toupper($0)}' ABCÅÄÖ $ echo ${str^^} # Bash 4.0 and later ABCÅÄÖ $ STR="ABCÅÄÖ" $ echo ${STR,,} abcåäö 
2
  • 2
    FWIW, tr '[:lower:]' '[:upper:]' is working now for your example on OS X at least (also with LC_ALL=sv_SE) Commented Mar 31, 2017 at 21:57
  • That's a limitation (and non-compliance) of GNU tr and a few others. tr '[:lower:]' '[:upper:]' is meant to work on any letter that have an uppercase variant in the locale, not just A-Z (whatever A-Z means, does it include É, Æ?). You have the same problem with some implementations of awk with toupper(), such as mawk, the default awk on Ubuntu. Commented Aug 18, 2023 at 7:42
10

Alternatively, you could switch to ksh or zsh which have had case conversion support for decades (long before bash's ${var^^} added in 4.0), though with a different syntax:

#! /bin/ksh - typeset -u upper="$1" printf '%s\n' "$upper" 

(also works with zsh; note that in pdksh/mksh, that only works for ASCII letters).

With zsh, you can also use the U parameter expansion flag or the csh style¹ u modifier:

#! /bin/zsh - printf '%s\n' "${(U)1}" "$1:u" 

POSIXLY, you can use:

awk -- 'BEGIN{print toupper(ARGV[1])}' "$1" 

There's also:

printf '%s\n' "$1" | tr '[:lower:]' '[:upper:]' 

But in a few implementations, including GNU tr, that only works for single-byte characters (so in UTF-8 locales, only on ASCII letters, not accented letters of the Latin alphabet, not ligatures, not letters from other alphabets that also have a concept of case such as the Greek or Cyrillic alphabets).

In practice, none of those work for characters that don't have a single-character uppercase version such as whose uppercase is FFI or the German ß. perl's uc (upper case) would work for those:

$ perl -CSA -le 'print uc for @ARGV' -- groß suffix GROSS SUFFIX 

Here with -CLSA to specify that the Stdio streams and Arguments are meant to be encoded in UTF-8 as long as UTF-8 is the charmap used in the Locale.


¹ though that modifier specifically is originally from zsh, already there in 1.0 from 1990, copied by tcsh later in 1992 for 6.01.03.

0

Try quoting the string:

echo "${str^^}" 
-1

If someone is still getting error trying ${str^^}, you can try python -c or perl It is likely because bash version is lower than 4.

But, so far bash 4 or over is working swiftly with the existing solution.

L2U="I will be upper" 

Using python -c in bash

python -c "print('$L2U'.upper())" I WILL BE UPPER 

Similarly it also can be used to capitalise with:

service="bootup.sh on home" python -c "print('$service'.capitalize())" Bootup.sh on home 

Using perl

echo $L2U | perl -ne 'print "\U$_"' I WILL BE UPPER 
1
  • 1
    While using python or perl here is a good idea, the fact that you embed the expansion of the shell variable in the code passed to those interpreters (in the python ones) make it a code injection vulnerability and is very bad practice. It may explain the downvotes you got. You also forgot the quotes around $L2U in the last one, and remember echo can't be used to output arbitrary data. Commented Aug 18, 2023 at 7:37

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.