14

Is there a way how to uppercase/lowercase only one character in some string?

Input Example:

syslog_apr_24_30
syslog_mar_01_17

Desired output:

syslog_Apr_24_30
syslog_Mar_01_17

Note please the uppercase beginning of the month.

I have tried awk but I'm not good enough to get it working.

8 Answers 8

19

You can use \u in GNU sed to uppercase a letter:

sed -e 's/_\(.\)/_\u\1/' input

Perl does the same:

perl -pe 's/_(.)/_\u$1/' input

\l does the opposite.

1
  • 8
    A touch simpler: sed 's/_./\U&/' Jun 23, 2015 at 16:16
5

Awk version with substrings and toupper

awk 'BEGIN{ FS=OFS="_"} {
        cap=toupper(substr($2,1,1));
        lower=substr($2,2,3);
        $2 = cap lower; print 
}' list.txt 

Sample run:

$ awk 'BEGIN{ FS=OFS="_"} { 
    cap=toupper(substr($2,1,1));
    lower=substr($2,2,3);$2 = cap lower; print 
}' list.txt               
syslog_Apr_24_30
syslog_Mar_01_17
0
4

awk:

echo "syslog_apr_24_30" | 
  awk -F'_' '{print $1"_"toupper(substr($2,1,1)) substr($2,2)  "_"$3"_"$4}'
4

Here's a Perl approach:

$ perl -pe 's/_./uc($&)/e' file
syslog_Apr_24_30
syslog_Mar_01_17

The -p causes each line to printed after applying the script given by -e. The substitution replaces the first instance of _ and the character following it with themselves ($& is whatever was matched) upper cased (uc()), The e at the end of the substitution operator (s///e) is needed in order to evaluate expressions.

3

Using awk:

awk -F_ '{
    printf "%s_%s_%s_%s",$1,toupper(substr($2,1,1))substr($2,2,2),$3,$4"\n"
}' foo

or

awk -F_ '{
    for(i=1;i<=NF;i++) {
        if(i==2){
            printf "%s",toupper(substr($i,1,1))substr($i,2,length($i)-1)
        } 
        else {printf "%s",$i} 
        if(i<NF) {printf "%s","_"}
    } printf "%s","\n"}' foo

Example

% cat foo
syslog_apr_24_30
syslog_mar_01_17

% awk -F_ '{for(i=1;i<=NF;i++) {if(i==2){printf "%s",toupper(substr($i,1,1))substr($i,2,length($i)-1)} else {printf "%s",$i} if(i<NF) {printf "%s","_"}} printf "%s","\n"}' foo
syslog_Apr_24_30
syslog_Mar_01_17

% awk -F_ '{printf "%s_%s_%s_%s",$1,toupper(substr($2,1,1))substr($2,2,2),$3,$4"\n"}' foo 
syslog_Apr_24_30
syslog_Mar_01_17
2

Another perl:

perl -F_ -anle '$F[1] = ucfirst $F[1];print join "_", @F'
1

Pure Bash 4.x, using a regex to pick out the part you want to upcase, and the ^^ upcase operator on that part. Tacking on the front and back (matched by .*) to re-create the whole string:

foo=syslog_apr_24_30
if [[ $foo =~ (.*)(_[a-z])(.*) ]]; then
    foo=${BASH_REMATCH[1]}${BASH_REMATCH[2]^^}${BASH_REMATCH[3]}
fi

If you don't remember all the quoting rules, it's safe to quote everything except the regex (which would make =~ do a literal string-match).

The ^ upcase-first operator only works at the beginning of a variable (or array element). And there doesn't seem to be any substring expansion that gives you what perl would call an lvalue (that you can assign to / modify). The up/downcase-first operators can take a pattern that's matched on a per-character basis, but that doesn't help skipping over syslog_, because there are month names that start with characters in "syslog".

Anyway, this might be faster than foo="$(echo "$foo" | sed 's/_./\U&/')" (posted as a comment to the accepted answer, by Glenn Jackman).

Bash, sed, or awk will be MANY times faster than perl. If you start finding multiple perl one-liners useful in a shell script, you should just write the whole thing in perl.

0

If the month always follows the first "_" (underscore), then use this (as shown in other answers):

sed -e 's/_\(.\)/_\u\1/'

If there may be other underscores before the one preceding the month then the above won't work.

If the month always begins with the 8th character, then use this:

sed -e 's/^\(.\{7\}\)\(.\)/\1\u\2/'

You must log in to answer this question.

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