awk ou sed para minúsculas / maiúsculas apenas um caractere na string?

13

Existe uma maneira de colocar em maiúsculas / minúsculas apenas um caractere em alguma string?

Exemplo de entrada:

syslog_apr_24_30
syslog_mar_01_17

Saída desejada:

syslog_Apr_24_30
syslog_Mar_01_17

Observe o início em maiúsculas do mês.

Eu tentei, awkmas não sou bom o suficiente para fazê-lo funcionar.

molni
fonte

Respostas:

18

Você pode usar \uno GNU sed para maiúsculas uma letra:

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

Perl faz o mesmo:

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

\l faz o oposto.

choroba
fonte
8
Um toque mais simples:sed 's/_./\U&/'
glenn jackman
4

awk:

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

Awk versão com substrings e toupper

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

Exemplo de execução:

$ 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
Sergiy Kolodyazhnyy
fonte
3

Usando awk:

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

ou

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

Exemplo

% 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
fonte
3

Aqui está uma abordagem Perl:

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

Isso -pfaz com que cada linha seja impressa após a aplicação do script fornecido por -e. A substituição substitui a primeira instância de _e o caractere que a segue por si mesmos ( $&é o que correspondeu) em maiúsculas ( uc()), eno final do operador de substituição ( s///e) é necessário para avaliar expressões.

terdon
fonte
2

Outro perl:

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

Pure Bash 4.x, usando uma regex para selecionar a parte que você deseja upcase e o ^^operador upcase nessa parte. Tachas na frente e nas costas (correspondidas por. *) Para recriar toda a sequência:

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

Se você não se lembrar de todas as regras de citação, é seguro citar tudo, exceto a expressão regular (o que faria =~uma correspondência literal de seqüência de caracteres).

O ^operador upcase-first funciona apenas no início de uma variável (ou elemento da matriz). E não parece haver nenhuma expansão de substring que fornece o que o perl chamaria de lvalue (que você pode atribuir / modificar). Os operadores up / downcase-first podem usar um padrão correspondente por caractere, mas isso não ajuda a pular syslog_, porque existem nomes de meses que começam com caracteres no "syslog".

De qualquer forma, isso pode ser mais rápido do que foo="$(echo "$foo" | sed 's/_./\U&/')" (postado como um comentário para a resposta aceita, por Glenn Jackman).

Bash, sed ou awk serão MUITO vezes mais rápidos que o perl. Se você começar a encontrar várias linhas de comando perl úteis em um script de shell, escreva tudo em perl.

Peter Cordes
fonte
0

Se o mês sempre seguir o primeiro "_" (sublinhado), use-o (como mostrado em outras respostas):

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

Se houver outros sublinhados antes do anterior ao mês, os itens acima não funcionarão.

Se o mês sempre começa com o 8º caractere, use o seguinte:

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