247

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

foo="bar";

//uppercase first character

echo $foo;

should print "Bar";

wpclevel
  • 2,451
  • 3
  • 14
  • 33
chovy
  • 72,281
  • 52
  • 227
  • 295
  • 1
    It's not an exact duplicate, but many of the techniques described in [Converting string to lower case in Bash shell scripting](http://stackoverflow.com/q/2264428/4154375) can also be applied to this problem. – pjh Feb 16 '16 at 19:57
  • 1
    While this question is about Bash, Zsh users can look at [*First character of a variable in a shell script to uppercase?*](https://stackoverflow.com/questions/12420317/first-character-of-a-variable-in-a-shell-script-to-uppercase). – Franklin Yu Dec 21 '20 at 23:12

17 Answers17

445

One way with bash (version 4+):

foo=bar
echo "${foo^}"

prints:

Bar
Michael Myers
  • 188,989
  • 46
  • 291
  • 292
Steve
  • 51,466
  • 13
  • 89
  • 103
  • 5
    Is there an opposite to this? – CMCDragonkai Dec 16 '15 at 12:06
  • 69
    @CMCDragonkai: To lowercase the first letter, use `"${foo,}"`. To lowercase all the letters, use `"${foo,,}"`. To uppercase all the letters, use `"${foo^^}"`. – Steve Dec 16 '15 at 21:37
  • @A-B-B: I'm not sure what you mean exactly, but you'd likely need to assign another variable if you're trying to substring `foo`. – Steve Oct 21 '16 at 00:59
  • This only capitalizes the first letter of the first word on multi word string. Not a full solution. – R J Apr 15 '18 at 02:30
  • 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 Feb 09 '19 at 13:16
  • How can I use the combination of , and ^ like if I want to smaller all the letters and then capitalise only the first one. Then it is not working `echo "${foo,,^}"` – KNDheeraj May 01 '19 at 07:35
  • @KNDheeraj: Best to use another variable for that, e.g.: `lc_foo="${foo,,}"; echo "${lc_foo^}"` – Steve May 01 '19 at 13:01
  • 24
    this doesn't seem to work on Macs. getting the following error: `bad substitution` – Toland Hon May 10 '19 at 16:42
  • @TolandHon: What's the output of `bash --version`? – Steve May 11 '19 at 02:56
  • @Steve GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18) – Toland Hon May 13 '19 at 16:26
  • 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 May 14 '19 at 00:20
  • Any idea how to do this with stdin - ie echo hello | ? – nevster May 29 '19 at 13:23
  • @nevster: Without putting stdin into a variable (which you probably don't want to do), you could use [tag:sed], [tag:awk] or [tag:perl]. E.g. `echo "hello world" | perl -ne 'print ucfirst'` prints `Hello world`. – Steve May 30 '19 at 01:41
  • Is it possible to achieve the same functionality on bash 3.2? @Michal's code works but is a bit too hacky. – wlnirvana Oct 30 '19 at 09:09
  • @wlnirvana: Michael Hoffman's answer is the best to my knowledge when working under Bash 3. You could try compartmentalizing Michael's code in a function. That might make your code more readable. HTH. – Steve Oct 30 '19 at 11:09
  • 1
    @stephanmg: version 4+ just means version 4 or greater. I.e. it works with versions 4 and 5, but not version 3. – Steve Sep 03 '20 at 10:08
  • 1
    Okay, then this does not work for my OSX bash which is version 4.3 or so. – stephanmg Sep 03 '20 at 10:13
  • 1
    @stephanmg: Is your default shell Zsh? `echo "${(C)foo}"` – Steve Sep 03 '20 at 11:29
  • 1
    @stephanmg: What is the error message you get? Can you provide a minimal example that reproduces the behaviour? I suspect your interpreter is not actually BASH version 4 or Zsh. – Steve Sep 03 '20 at 23:32
  • @Steve I presume too. It is Bash provided by OSX, so there might be differences. RIght now I cannot provide the example. Sorry. – stephanmg Sep 04 '20 at 07:18
  • @TolandHon: I think you may not have this problem anymore, if the problem still presist, please try to run the bash filename.sh. this will solve the error "bad substitution" – ABoringAI Feb 06 '22 at 01:48
  • Source: https://www.gnu.org/s/bash/manual/html_node/Shell-Parameter-Expansion.html – Dante Oct 01 '22 at 16:16
214
foo="$(tr '[:lower:]' '[:upper:]' <<< ${foo:0:1})${foo:1}"
Michael Hoffman
  • 32,526
  • 7
  • 64
  • 86
  • 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? – Krzysztof Jabłoński Feb 26 '16 at 15:48
  • 17
    @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 Apr 13 '16 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}) – Itai Hanski Jul 06 '16 at 07: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. – Jesse Chisholm Jul 27 '16 at 20:54
  • @JesseChisholm true - my script is not camel case friendly, but intentionally so. – Itai Hanski Jul 28 '16 at 07:34
  • Your are using a lot of bashism like `<<<` and string range, and this is not a good thing for portable scripts. – Daniele Orlando Jan 05 '17 at 19:43
  • 1
    Works only on ASCII – Bell Feb 27 '17 at 00:23
  • 3
    @DanieleOrlando, true, but this question has no tags other than `bash`. – Charles Duffy Mar 10 '17 at 03:48
  • 1
    @Bell, that depends on whether your local platform's implementation of `tr` is multi-byte capable. The GNU one isn't, but BSD, Solaris and MacOS `tr` implementations are fine. – Charles Duffy Mar 10 '17 at 03:50
42

One way with sed:

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

Prints:

Bar
Steve
  • 51,466
  • 13
  • 89
  • 103
29
$ foo="bar";
$ foo=`echo ${foo:0:1} | tr  '[a-z]' '[A-Z]'`${foo:1}
$ echo $foo
Bar
Majid Laissi
  • 19,188
  • 19
  • 68
  • 105
28

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+)

Noam Manos
  • 15,216
  • 3
  • 86
  • 85
Ole Lukøje
  • 562
  • 5
  • 8
  • 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 Dec 16 '18 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 Jul 21 '20 at 15:03
  • This is simple and perfect. – DrBeco Jun 15 '21 at 20:22
18

Using awk only

foo="uNcapItalizedstrIng"
echo $foo | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}'
toske
  • 1,744
  • 13
  • 24
15

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
Equin0x
  • 159
  • 1
  • 2
  • 1
    finally something that works with a variable and on Mac! Thank you! – OZZIE Jan 20 '17 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. – Charles Duffy Mar 08 '17 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 Nov 11 '22 at 20:51
13

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&/'
Freeman
  • 9,464
  • 7
  • 35
  • 58
6

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}"
Michał Górny
  • 18,713
  • 5
  • 53
  • 76
  • how to define foo? I tried `foo = $1` but I only get `-bash: foo: command not found` – OZZIE Jan 20 '17 at 16:01
  • 1
    @OZZIE, no spaces around the `=` in assignments. This is something that http://shellcheck.net/ will find for you programatically. – Charles Duffy Mar 08 '17 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 Mar 09 '17 at 07: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`?) – Charles Duffy Mar 10 '17 at 03:38
6

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:

double-beep
  • 5,031
  • 17
  • 33
  • 41
zetaomegagon
  • 61
  • 1
  • 2
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
Shemeshey
  • 551
  • 4
  • 7
1

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

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

returns Abc

Thomas Ducrot
  • 2,841
  • 1
  • 16
  • 14
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"
cjfp
  • 153
  • 1
  • 9
0

Posix compliant and with less sub-processes:

v="foo[Bar]"
printf "%s" "${v%"${v#?}"}" | tr '[:lower:]' '[:upper:]' && printf "%s" "${v#?}"
==> Foo[Bar]
nowoe
  • 41
  • 4
-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 }

user123456
  • 364
  • 1
  • 3
  • 16
-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
Ivan
  • 6,188
  • 1
  • 16
  • 23
-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=
Roger
  • 8,286
  • 17
  • 59
  • 77
  • 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). – gniourf_gniourf Mar 08 '17 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. – gniourf_gniourf Mar 09 '17 at 09:44
  • Better written as: `if [[ $S =~ ^([^[:alpha:]]*)([[:alpha:]])(.*) ]]; then out=${BASH_REMATCH[1]}$(tr '[[:lower:]]' '[[:upper:]]' <<<"${BASH_REMATCH[2]}")${BASH_REMATCH[3]}; else out=$S; fi; printf '%s\n' "$out"` – gniourf_gniourf Mar 09 '17 at 09:50
  • 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"` – gniourf_gniourf Mar 09 '17 at 09:51
  • More light on the subject: "when "binary operator, ‘=~’, is available, **it's perfectly safe to leave variable expansions unquoted** on the left-hand side, but you need to know that variable expansions will happen on the right-hand side." http://stackoverflow.com/a/18710850/559742 – Roger Mar 09 '17 at 10:34
  • 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 http://shellcheck.net/ finds until this code comes up clean. – Charles Duffy Mar 10 '17 at 03:40
  • 2
    As for the language you quoted from http://unix.stackexchange.com/questions/68694/when-is-double-quoting-necessary/68748#68748, 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](http://mywiki.wooledge.org/BashPitfalls#echo_.24foo). – Charles Duffy Mar 10 '17 at 03:45
  • 1
    (It's also still using deprecated syntax. `N=$[$N+1]` isn't just a stylistic choice without any functional impact: It's actually **less portable** than the POSIX-standardized `N=$((N+1))` syntax that replaces it, as it's a holdout from 1970s-era Bourne that wasn't added to the 1990s-era POSIX standard and is not guaranteed to be present in modern-day POSIX-based shells). – Charles Duffy Mar 10 '17 at 03:57