awk lub sed na małe / wielkie litery tylko jeden znak w ciągu?

13

Czy istnieje sposób, w jaki wielkie / małe litery powinny zawierać tylko jeden znak w jakimś ciągu?

Przykład wprowadzania:

syslog_apr_24_30
syslog_mar_01_17

Pożądane wyjście:

syslog_Apr_24_30
syslog_Mar_01_17

Uwaga: proszę wpisać wielkie litery na początku miesiąca.

Próbowałem, awkale nie jestem wystarczająco dobry, aby uruchomić.

molni
źródło

Odpowiedzi:

18

W \uGNU sed możesz użyć wielkiej litery:

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

Perl robi to samo:

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

\l robi coś przeciwnego.

choroba
źródło
8
Dotyk prostszy: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
źródło
3

Awk wersja z podciągami i nakładką

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

Przykładowy przebieg:

$ 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
źródło
3

Używanie awk:

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

lub

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

Przykład

% 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
źródło
3

Oto podejście Perla:

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

-pPowoduje każdą linię do drukowany po zastosowaniu skryptu podane przez -e. Podstawienie zastępuje pierwsze wystąpienie, _a znak po nim ze sobą ( $&niezależnie od tego, co zostało dopasowane) wielkimi literami ( uc()), Na ekońcu operatora podstawienia ( s///e) jest potrzebny do oceny wyrażeń.

terdon
źródło
2

Kolejny perl:

perl -F_ -anle '$F[1] = ucfirst $F[1];print join "_", @F'
Cuonglm
źródło
1

Pure Bash 4.x, używając wyrażenia regularnego, aby wybrać część, którą chcesz upcase, i ^^operator upcase na tej części. Sczepianie z przodu iz tyłu (dopasowane przez. *) W celu odtworzenia całego łańcucha:

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

Jeśli nie pamiętasz wszystkich reguł cytowania, możesz bezpiecznie cytować wszystko oprócz wyrażenia regularnego (co spowodowałoby =~dosłowne dopasowanie łańcucha).

Operator ^upcase-first działa tylko na początku zmiennej (lub elementu tablicy). I wydaje się, że nie ma żadnego rozszerzenia podłań, które daje to, co perl nazwałby lvalue (które można przypisać / zmodyfikować). Operatorzy up / down-first mogą przyjąć wzorzec, który jest dopasowywany dla poszczególnych znaków, ale to nie pomaga pominąć syslog_, ponieważ istnieją nazwy miesięcy zaczynające się od znaków w „syslog”.

W każdym razie może to być szybsze niż foo="$(echo "$foo" | sed 's/_./\U&/')"(opublikowane jako komentarz do zaakceptowanej odpowiedzi przez Glenna Jackmana).

Bash, sed lub awk będą WIELE razy szybsze niż perl. Jeśli zaczniesz znajdować wiele linijek perlowych przydatnych w skrypcie powłoki, powinieneś po prostu napisać całą rzecz w perlu.

Peter Cordes
źródło
0

Jeśli miesiąc zawsze następuje po pierwszym „_” (podkreślenie), użyj tego (jak pokazano w innych odpowiedziach):

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

Jeśli przed poprzednim miesiącem mogą występować inne podkreślenia, powyższe nie będzie działać.

Jeśli miesiąc zawsze zaczyna się od ósmej litery, użyj tego:

sed -e 's/^\(.\{7\}\)\(.\)/\1\u\2/'
Kevin Fegan
źródło