awk или sed в нижнем / верхнем регистре только один символ в строке?

13

Есть ли способ, как в верхнем / нижнем регистре только один символ в некоторой строке?

Пример ввода:

syslog_apr_24_30
syslog_mar_01_17

Желаемый вывод:

syslog_Apr_24_30
syslog_Mar_01_17

Обратите внимание, пожалуйста, на верхний регистр начала месяца.

Я пытался, awkно я не достаточно хорош, чтобы заставить его работать.

molni
источник

Ответы:

18

Вы можете использовать \uв GNU sed прописные буквы:

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

Perl делает то же самое:

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

\l делает обратное.

choroba
источник
8
Немного проще:sed 's/_./\U&/'
Гленн Джекман
4

AWK:

echo "syslog_apr_24_30" | 
  awk -F'_' '{print $1"_"toupper(substr($2,1,1)) substr($2,2)  "_"$3"_"$4}'
Майкл Даррант
источник
3

Awk версия с подстрокой и таппером

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

Образец прогона:

$ 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
Сергей Колодяжный
источник
3

Использование awk:

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

или

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

пример

% 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
AB
источник
3

Вот подход Perl:

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

-pЗаставляет каждую строку печатается после применения сценария дается -e. Подстановка заменяет первый экземпляр _и последующий за ним символ ( $&независимо от того, что было сопоставлено) в верхнем регистре ( uc()). eОператор подстановки в конце ( s///e) необходим для оценки выражений.

Тердон
источник
2

Другое perl:

perl -F_ -anle '$F[1] = ucfirst $F[1];print join "_", @F'
cuonglm
источник
1

Pure Bash 4.x, используя регулярное выражение для выбора части, которую вы хотите выделить, и ^^оператора upcase для этой части. Прикрепление спереди и сзади (соответствует. *) Для воссоздания всей строки:

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

Если вы не помните все правила цитирования, можно цитировать все, кроме регулярных выражений (что приведет =~к буквальному совпадению строк).

Оператор ^upcase-first работает только в начале переменной (или элемента массива). И, кажется, нет никакого раскрытия подстроки, которое дает вам то, что perl назвал бы lvalue (которое вы можете назначить / изменить). Операторы up / downcase-first могут принимать шаблон, который сопоставляется для каждого символа, но это не помогает пропускать syslog_, потому что есть названия месяцев, которые начинаются с символов в «syslog».

Во всяком случае, это может быть быстрее, чем foo="$(echo "$foo" | sed 's/_./\U&/')"(опубликовано в качестве комментария к принятому ответу, Гленн Джекман).

Bash, Sed или WKK будет во много раз быстрее, чем Perl. Если вы начинаете находить несколько perl-однострочников полезными в сценарии оболочки, вам просто нужно написать все это на perl.

Питер Кордес
источник
0

Если месяц всегда следует за первым «_» (подчеркивание), используйте это (как показано в других ответах):

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

Если перед предыдущим месяцем могут быть другие подчеркивания, то вышеприведенное не сработает.

Если месяц всегда начинается с 8-го символа, используйте это:

sed -e 's/^\(.\{7\}\)\(.\)/\1\u\2/'
Кевин Феган
источник