264

I want to uppercase just the first character in my string with bash.

foo="bar";

//uppercase first character

echo $foo;

should print Bar.

2

17 Answers 17

463

One way with bash (version 4+):

foo=bar
echo "${foo^}"

prints:

Bar
22
  • 5
    Is there an opposite to this? Commented Dec 16, 2015 at 12:06
  • 72
    @CMCDragonkai: To lowercase the first letter, use "${foo,}". To lowercase all the letters, use "${foo,,}". To uppercase all the letters, use "${foo^^}".
    – Steve
    Commented Dec 16, 2015 at 21:37
  • 1
    I accidentally noticed that ${foo~} and ${foo~~} have the same effect as ${foo^} and ${foo^^}. But I never saw that alternative mentioned anywhere.
    – mivk
    Commented Feb 9, 2019 at 13:16
  • 26
    this doesn't seem to work on Macs. getting the following error: bad substitution
    – Toland Hon
    Commented May 10, 2019 at 16:42
  • 4
    @TolandHon: As you've probably already worked out, you'll need to upgrade to version 4.x, or try another solution if that machine can't be upgraded for some reason.
    – Steve
    Commented May 14, 2019 at 0:20
222
foo="$(tr '[:lower:]' '[:upper:]' <<< ${foo:0:1})${foo:1}"
11
  • 4
    Despite being more complex than the best scored answer, this one actually does exactly that: 'uppercase first character in a variable'. Best scored answer does not have that results. Looks like simple answers are upvoted more willingly than the correct ones? Commented Feb 26, 2016 at 15:48
  • 18
    @KrzysztofJabłoński: Actually, the "best scored answer" below will produce the same results as this answer under Bash 4. This answer has the overhead of calling a subshell (another shell), the overhead of calling the 'tr' utility (another process, not Bash), the overhead of using a here-doc/string (temporary file creation) and it uses two substitutions compared to the best scored answer's one. If you absolutely must write legacy code, consider using a function instead of a subshell.
    – Steve
    Commented Apr 13, 2016 at 23:57
  • 3
    This answer assumes the input is all lower case. This variant has no assumptions: $(tr '[:lower:]' '[:upper:]' <<< ${foo:0:1})$(tr '[:upper:]' '[:lower:]' <<< ${foo:1}) Commented Jul 6, 2016 at 7:18
  • 7
    @ItaiHanski The OP did not specify what should happen to the remaining characters, only the first. Supposed they wanted to change notQuiteCamel into NotQuiteCamel. Your variant has the side effect or forcing the remaining to lowercase - at the cost of doubling the number of sub shells and tr processes. Commented Jul 27, 2016 at 20:54
  • 3
    @DanieleOrlando, true, but this question has no tags other than bash. Commented Mar 10, 2017 at 3:48
43

One way with sed:

echo "$(echo "$foo" | sed 's/.*/\u&/')"

Prints:

Bar
10
  • 13
    doesn't work on MacOS10 Lion sed, sigh, the \u expands to u instead of being an operator.
    – vwvan
    Commented Jun 7, 2014 at 1:36
  • 2
    @vwvan brew install coreutils gnu-sed and follow the instructions to use the commands without the prefix g. For what it's worth I've never wanted to run the OSX version of these commands since getting the GNU versions.
    – Dean
    Commented Aug 26, 2014 at 19:01
  • 3
    @Dean: FWIW, I've never wanted to run OSX :-)
    – Steve
    Commented Aug 26, 2014 at 22:41
  • 6
    Just change it to sed 's/./\U&/ and it will work on POSIX sed. Commented May 26, 2017 at 15:29
  • 6
    @DavidConrad echo "bar" | sed 's/./\U&/' produces Ubar on mac. I think \U is not POSIX standard but a GNU extension.
    – wlnirvana
    Commented Aug 6, 2020 at 1:30
37

To capitalize first word only:

foo='one two three'
foo="${foo^}"
echo $foo

One two three


To capitalize every word in the variable:

foo="one two three"
foo=( $foo ) # without quotes
foo="${foo[@]^}"
echo $foo

One Two Three


(works in bash 4+)

3
  • 1
    foo='one two three'; foo=$(for i in $foo; do echo -n "${i^} "; done) Shorter resolution for every word. foo Now is "One Two Three"
    – vr286
    Commented Dec 16, 2018 at 16:11
  • How can I capitalize first 3 or 4 letters to uppercase ? Like pqrs-123-v1 to PQRS-123-v1 ? Or you can say, all letters before first hyphen...
    – Vicky Dev
    Commented Jul 21, 2020 at 15:03
  • This is simple and perfect.
    – DrBeco
    Commented Jun 15, 2021 at 20:22
29
$ foo="bar";
$ foo=`echo ${foo:0:1} | tr  '[a-z]' '[A-Z]'`${foo:1}
$ echo $foo
Bar
18

Using awk only

foo="uNcapItalizedstrIng"
echo $foo | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}'
2
  • 1
    result: Uncapitalizedstring Commented Jul 22, 2019 at 14:43
  • looking at this one like 👁️👄👁️
    – Jameson
    Commented Aug 12, 2020 at 2:59
16

Here is the "native" text tools way:

#!/bin/bash

string="abcd"
first=`echo $string|cut -c1|tr [a-z] [A-Z]`
second=`echo $string|cut -c2-`
echo $first$second
3
  • 2
    finally something that works with a variable and on Mac! Thank you!
    – OZZIE
    Commented Jan 20, 2017 at 16:02
  • 5
    @DanieleOrlando, not as portable as one might hope. tr [:lower:] [:upper:] is a better choice if it needs to work in language/locales incorporating letters that aren't in the a-z or A-Z ranges. Commented Mar 8, 2017 at 18:01
  • Nice unix tool answer. Although yes what Charles said. In fact, in the man page of tr, it says: The example of Translate the contents of file1 to upper-case. is tr "[:lower:]" "[:upper:]" < file1 and that (This should be preferred over the traditional UNIX idiom of “tr a-z A-Z”, since it works correctly in all locales.).
    – RexYuan
    Commented Nov 11, 2022 at 20:51
15

just for fun here you are :

foo="bar";    

echo $foo | awk '{$1=toupper(substr($1,0,1))substr($1,2)}1'
# or
echo ${foo^}
# or
echo $foo | head -c 1 | tr [a-z] [A-Z]; echo $foo | tail -c +2
# or
echo ${foo:1} | sed -e 's/^./\B&/'
4
  • 1
    Thank you for, AFAICT, the only solutions that don't involve GNUisms.
    – zwol
    Commented Oct 7, 2020 at 18:29
  • 2
    (Specifically, the awk solution works with any POSIX-compliant awk.)
    – zwol
    Commented Oct 7, 2020 at 18:33
  • 2
    This works on MacOS and with ZSH.
    – John Smith
    Commented Dec 5, 2021 at 7:43
  • The fourth example only works for strings starting with the letter B. This does make it slightly less versatile than other answers on this page.
    – Chris
    Commented Jan 24, 2023 at 8:03
7

It can be done in pure bash with bash-3.2 as well:

# First, get the first character.
fl=${foo:0:1}

# Safety check: it must be a letter :).
if [[ ${fl} == [a-z] ]]; then
    # Now, obtain its octal value using printf (builtin).
    ord=$(printf '%o' "'${fl}")

    # Fun fact: [a-z] maps onto 0141..0172. [A-Z] is 0101..0132.
    # We can use decimal '- 40' to get the expected result!
    ord=$(( ord - 40 ))

    # Finally, map the new value back to a character.
    fl=$(printf '%b' '\'${ord})
fi

echo "${fl}${foo:1}"
4
  • how to define foo? I tried foo = $1 but I only get -bash: foo: command not found
    – OZZIE
    Commented Jan 20, 2017 at 16:01
  • 2
    @OZZIE, no spaces around the = in assignments. This is something that shellcheck.net will find for you programatically. Commented Mar 8, 2017 at 18:00
  • I always forget that about shell scripts.. only language where one space (before/after) matters... sigh (python is at least 4 if I remember correctly)
    – OZZIE
    Commented Mar 9, 2017 at 7:19
  • @OZZIE, it has to matter, because if it didn't matter, you couldn't pass = as an argument to a program (how is it supposed to know if someprogram = is running someprogram, or assigning to a variable named someprogram?) Commented Mar 10, 2017 at 3:38
7

This works too...

FooBar=baz

echo ${FooBar^^${FooBar:0:1}}

=> Baz
FooBar=baz

echo ${FooBar^^${FooBar:1:1}}

=> bAz
FooBar=baz

echo ${FooBar^^${FooBar:2:2}}

=> baZ

And so on.

Sources:

Inroductions/Tutorials:

3

Alternative and clean solution for both Linux and OSX, it can also be used with bash variables

python -c "print(\"abc\".capitalize())"

returns Abc

2

This one worked for me:

Searching for all *php file in the current directory , and replace the first character of each filename to capital letter:

e.g: test.php => Test.php

for f in *php ; do mv "$f" "$(\sed 's/.*/\u&/' <<< "$f")" ; done
1
  • 7
    The original question was a string, and made no reference to renaming files.
    – AndySavage
    Commented Feb 20, 2017 at 18:45
0

This is POSIX sh-compatible as far as I know.

upper_first.sh:

#!/bin/sh

printf "$1" | cut -c1 -z | tr -d '\0' | tr [:lower:] [:upper:]
printf "$1" | cut -c2-

cut -c1 -z ends the first string with \0 instead of \n. It gets removed with tr -d '\0'. It also works to omit the -z and use tr -d '\n' instead, but this breaks if the first character of the string is a newline.

Usage:

$ upper_first.sh foo
Foo
$

In a function:

#!/bin/sh

function upper_first ()
{
    printf "$1" | cut -c1 -z | tr -d '\0' | tr [:lower:] [:upper:]
    printf "$1" | cut -c2-
}

old="foo"
new="$(upper_first "$old")"
echo "$new"
0

Posix compliant and with less sub-processes:

v="foo[Bar]"
printf "%s" "${v%"${v#?}"}" | tr '[:lower:]' '[:upper:]' && printf "%s" "${v#?}"
==> Foo[Bar]
-1
first-letter-to-lower () {
        str="" 
        space=" " 
        for i in $@
        do
                if [ -z $(echo $i | grep "the\|of\|with" ) ]
                then
                        str=$str"$(echo ${i:0:1} | tr  '[A-Z]' '[a-z]')${i:1}$space" 
                else
                        str=$str${i}$space 
                fi
        done
        echo $str
}
first-letter-to-upper-xc () {
        v-first-letter-to-upper | xclip -selection clipboard
}
first-letter-to-upper () {
        str="" 
        space=" " 
        for i in $@
        do
                if [ -z $(echo $i | grep "the\|of\|with" ) ]
                then
                        str=$str"$(echo ${i:0:1} | tr  '[a-z]' '[A-Z]')${i:1}$space" 
                else
                        str=$str${i}$space 
                fi
        done
        echo $str
}

first-letter-to-lower-xc(){ v-first-letter-to-lower | xclip -selection clipboard }

-4

Not exactly what asked but quite helpful

declare -u foo #When the variable is assigned a value, all lower-case characters are converted to upper-case.

foo=bar
echo $foo
BAR

And the opposite

declare -l foo #When the variable is assigned a value, all upper-case characters are converted to lower-case.

foo=BAR
echo $foo
bar
-7

What if the first character is not a letter (but a tab, a space, and a escaped double quote)? We'd better test it until we find a letter! So:

S='  \"ó foo bar\"'
N=0
until [[ ${S:$N:1} =~ [[:alpha:]] ]]; do N=$[$N+1]; done
#F=`echo ${S:$N:1} | tr [:lower:] [:upper:]`
#F=`echo ${S:$N:1} | sed -E -e 's/./\u&/'` #other option
F=`echo ${S:$N:1}
F=`echo ${F} #pure Bash solution to "upper"
echo "$F"${S:(($N+1))} #without garbage
echo '='${S:0:(($N))}"$F"${S:(($N+1))}'=' #garbage preserved

Foo bar
= \"Foo bar=
8
  • 6
    Please write cleaner code! you're lacking quotes all over the place. You're using obsolete syntax (backticks); you're using antiquated syntax ($[...]). You're using lots of useless parentheses. You're assuming that the string consists of characters from the latin alphabet (how about accentuated letters, or letters in other alphabets?). You have a great deal of work to render this snippet usable! (and since you're using regex, you might as well use the regex to find the first letter, instead of a loop). Commented Mar 8, 2017 at 18:05
  • 6
    I think you're wrong. It's the obligatory comment that accompanies the downvote, explaining all the wrong patterns and misconceptions of the answer. Please, learn from your mistakes (and before that, try to understand them) instead of being stubborn. Being a good fellow is also incompatible with spreading bad practices. Commented Mar 9, 2017 at 9:44
  • 1
    Or, if using Bash ≥4, you don't need tr: if [[ $S =~ ^([^[:alpha:]]*)([[:alpha:]].*) ]]; then out=${BASH_REMATCH[1]}${BASH_REMATCH[2]^}; else out=$S; fi; printf '%s\n' "$out" Commented Mar 9, 2017 at 9:51
  • 5
    This code is still badly broken. It's still using backticks instead of modern substitution syntax; it still has unmatched quotes; it still has missing quotes in places where they do in fact matter; etc. Try putting some whitespace-surrounded glob characters into your test data, if you don't believe that those missing quotes make a difference, and fix the issues shellcheck.net finds until this code comes up clean. Commented Mar 10, 2017 at 3:40
  • 2
    As for the language you quoted from unix.stackexchange.com/questions/68694/…, what you miss is that echo $foo is an example of a situation where the parser expects a list of words; so in your echo ${F}, the contents of F are string-split and glob-expanded. That's similarly true for the echo ${S:$N:1} example and all the rest. See BashPitfalls #14. Commented Mar 10, 2017 at 3:45

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