272

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

478

One way with bash (version 4+):

foo=bar
echo "${foo^}"

prints:

Bar
Sign up to request clarification or add additional context in comments.

22 Comments

Is there an opposite to this?
@CMCDragonkai: To lowercase the first letter, use "${foo,}". To lowercase all the letters, use "${foo,,}". To uppercase all the letters, use "${foo^^}".
I accidentally noticed that ${foo~} and ${foo~~} have the same effect as ${foo^} and ${foo^^}. But I never saw that alternative mentioned anywhere.
this doesn't seem to work on Macs. getting the following error: bad substitution
@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.
|
229
foo="$(tr '[:lower:]' '[:upper:]' <<< ${foo:0:1})${foo:1}"

12 Comments

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?
@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.
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})
@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.
@DanieleOrlando, true, but this question has no tags other than bash.
|
45

One way with sed:

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

Prints:

Bar

10 Comments

doesn't work on MacOS10 Lion sed, sigh, the \u expands to u instead of being an operator.
@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: FWIW, I've never wanted to run OSX :-)
Just change it to sed 's/./\U&/ and it will work on POSIX sed.
@DavidConrad echo "bar" | sed 's/./\U&/' produces Ubar on mac. I think \U is not POSIX standard but a GNU extension.
|
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+)

4 Comments

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"
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...
This is simple and perfect.
What if the variable is in uppercase already?... Is it possible to lowercase everything except the first letter?
29
$ foo="bar";
$ foo=`echo ${foo:0:1} | tr  '[a-z]' '[A-Z]'`${foo:1}
$ echo $foo
Bar

Comments

18

Using awk only

foo="uNcapItalizedstrIng"
echo $foo | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}'

2 Comments

result: Uncapitalizedstring
looking at this one like 👁️👄👁️
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 Comments

finally something that works with a variable and on Mac! Thank you!
@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.
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.).
16

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 Comments

Thank you for, AFAICT, the only solutions that don't involve GNUisms.
(Specifically, the awk solution works with any POSIX-compliant awk.)
This works on MacOS and with ZSH.
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.
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 Comments

how to define foo? I tried foo = $1 but I only get -bash: foo: command not found
@OZZIE, no spaces around the = in assignments. This is something that shellcheck.net will find for you programatically.
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, 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?)
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:

Comments

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

Comments

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 Comment

The original question was a string, and made no reference to renaming files.
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"

Comments

0

Posix compliant and with less sub-processes:

v="foo[Bar]"
printf "%s" "${v%"${v#?}"}" | tr '[:lower:]' '[:upper:]' && printf "%s" "${v#?}"
==> Foo[Bar]

Comments

-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 }

Comments

-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

Comments

-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 Comments

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).
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.
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"
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.
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.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.