Jak przekonwertować ciąg na małe litery w Bash?


Odpowiedzi:


2180

Istnieją różne sposoby:

Standard POSIX

tr

$ echo "$a" | tr '[:upper:]' '[:lower:]'
hi all

AWK

$ echo "$a" | awk '{print tolower($0)}'
hi all

Bez POSIX

Mogą wystąpić problemy z przenośnością z następującymi przykładami:

Bash 4.0

$ echo "${a,,}"
hi all

sed

$ echo "$a" | sed -e 's/\(.*\)/\L\1/'
hi all
# this also works:
$ sed -e 's/\(.*\)/\L\1/' <<< "$a"
hi all

Perl

$ echo "$a" | perl -ne 'print lc'
hi all

Grzmotnąć

lc(){
    case "$1" in
        [A-Z])
        n=$(printf "%d" "'$1")
        n=$((n+32))
        printf \\$(printf "%o" "$n")
        ;;
        *)
        printf "%s" "$1"
        ;;
    esac
}
word="I Love Bash"
for((i=0;i<${#word};i++))
do
    ch="${word:$i:1}"
    lc "$ch"
done

Uwaga: YMMV w tym przypadku. Nie działa dla mnie (GNU bash w wersji 4.2.46 i 4.0.33 (i to samo zachowanie 2.05b.0, ale nocasematch nie jest zaimplementowany)) nawet przy użyciu shopt -u nocasematch;. Wyłączenie tej opcji nocasematch powoduje, że [["fooBaR" == "FOObar"]] pasuje do OK ALE dziwnie wewnątrz skrzynki [bz] są niepoprawnie dopasowane do [AZ] Bash jest zdezorientowany podwójnie ujemnym („niepokojącym nocasematch”)! :-)


9
Czy coś pomijam, czy może twój ostatni przykład (w Bash) robi coś zupełnie innego? Działa dla „ABX”, ale jeśli zamiast tego zrobisz word="Hi All"podobnie jak w innych przykładach, to hanie hi all. Działa tylko w przypadku wielkich liter i pomija już małe litery.
jangosteve

26
Zauważ, że tylko tri awkprzykłady są określone w standardzie POSIX.
Richard Hansen

178
tr '[:upper:]' '[:lower:]'użyje bieżących ustawień narodowych do określenia odpowiedników wielkich / małych liter, więc będzie działać z ustawieniami narodowymi, które używają liter ze znakami diakrytycznymi.
Richard Hansen

10
Jak uzyskać wynik w nowej zmiennej? Tzn. Chcę, aby ciąg małych liter był nową zmienną?
Adam Parkin,

60
@Adam:b="$(echo $a | tr '[A-Z]' '[a-z]')"
Tino

434

W Bash 4:

Na małe litery

$ string="A FEW WORDS"
$ echo "${string,}"
a FEW WORDS
$ echo "${string,,}"
a few words
$ echo "${string,,[AEIUO]}"
a FeW WoRDS

$ string="A Few Words"
$ declare -l string
$ string=$string; echo "$string"
a few words

Do wielkich liter

$ string="a few words"
$ echo "${string^}"
A few words
$ echo "${string^^}"
A FEW WORDS
$ echo "${string^^[aeiou]}"
A fEw wOrds

$ string="A Few Words"
$ declare -u string
$ string=$string; echo "$string"
A FEW WORDS

Przełącz (nieudokumentowane, ale opcjonalnie konfigurowalne w czasie kompilacji)

$ string="A Few Words"
$ echo "${string~~}"
a fEW wORDS
$ string="A FEW WORDS"
$ echo "${string~}"
a FEW WORDS
$ string="a few words"
$ echo "${string~}"
A few words

Wielkie litery (nieudokumentowane, ale opcjonalnie konfigurowalne w czasie kompilacji)

$ string="a few words"
$ declare -c string
$ string=$string
$ echo "$string"
A few words

Tytuł sprawy:

$ string="a few words"
$ string=($string)
$ string="${string[@]^}"
$ echo "$string"
A Few Words

$ declare -c string
$ string=(a few words)
$ echo "${string[@]}"
A Few Words

$ string="a FeW WOrdS"
$ string=${string,,}
$ string=${string~}
$ echo "$string"
A few words

Aby wyłączyć declareatrybut, użyj +. Na przykład declare +c string. Wpływa to na kolejne przypisania, a nie na bieżącą wartość.

Te declareopcje zmienić atrybut zmiennej, ale nie zawartość. Ponowne przypisania w moich przykładach aktualizują zawartość, aby pokazać zmiany.

Edytować:

Dodano „przełącz pierwszy znak po słowie” ( ${var~}) zgodnie z sugestią ghostdog74 .

Edycja: Poprawiono zachowanie tyldy, aby pasowało do Bash 4.3.


5
Dość bizzare, operatory „^^” i „,,” nie działają na znakach spoza ASCII, ale „~~” działa… Zwróci więc string="łódź"; echo ${string~~}„ŁÓDŹ”, ale echo ${string^^}zwróci „łóDź”. Nawet w LC_ALL=pl_PL.utf-8. To używa bash 4.2.24.
Hubert Kario

2
@HubertKario: To dziwne. To samo dla mnie w Bash 4.0.33 z tym samym ciągiem w en_US.UTF-8. To błąd i zgłosiłem go.
Wstrzymano do odwołania.

1
@HubertKario: Spróbuj echo "$string" | tr '[:lower:]' '[:upper:]'. Prawdopodobnie wykaże tę samą awarię. Tak więc problem przynajmniej częściowo nie dotyczy Basha.
Wstrzymano do odwołania.

1
@DennisWilliamson: Tak, też to zauważyłem (patrz komentarz do odpowiedzi Shuvalova). Powiedziałbym tylko: „te rzeczy są tylko dla ASCII”, ale potem działa operator „~~”, więc to nie tak, że nie ma już tabel kodu i tłumaczeń ...
Hubert Kario

4
@HubertKario: Opiekun Bash potwierdził błąd i stwierdził, że zostanie on naprawiony w następnej wersji.
Wstrzymano do odwołania.

123
echo "Hi All" | tr "[:upper:]" "[:lower:]"

4
@RichardHansen: trnie działa dla mnie dla znaków spoza ACII. Mam prawidłowy zestaw ustawień regionalnych i wygenerowane pliki ustawień narodowych. Masz pojęcie, co mogę zrobić źle?
Hubert Kario

FYI: To działało na Windows / Msys. Niektóre inne sugestie nie.
wasatchwizard

3
Dlaczego jest [:upper:]potrzebny
mgutt

77

tr :

a="$(tr [A-Z] [a-z] <<< "$a")"

AWK :

{ print tolower($0) }

sed :

y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/

2
+1 a="$(tr [A-Z] [a-z] <<< "$a")"wygląda dla mnie najłatwiej. Nadal jestem początkującym ...
Sandeepan Nath

2
Zdecydowanie polecam sedrozwiązanie; Pracowałem w środowisku, które z jakiegoś powodu nie ma, trale jeszcze nie znalazłem systemu bez sed, a poza tym dużo czasu chcę to zrobić. Właśnie zrobiłem coś innego, sedwięc mogę połączyć polecenia razem w jedną (długą) instrukcję.
Haravikk,

2
Wyrażenia w nawiasach powinny być cytowane. W tr [A-Z] [a-z] Apowłoce może wykonać rozszerzenie nazwy pliku, jeśli istnieją nazwy plików składające się z jednej litery lub ustawiono nullgob . tr "[A-Z]" "[a-z]" Abędzie się zachowywał poprawnie.
Dennis,

2
@CamiloMartin to system BusyBox, w którym mam ten problem, szczególnie Synology NAS, ale napotkałem go również na kilku innych systemach. Ostatnio robiłem wiele skryptów powłoki między platformami, a wymaganie, aby nic więcej nie było instalowane, czyni to bardzo trudnym! Jednak jeszcze nie spotkałem systemu bezsed
Haravikk

2
Pamiętaj, że tr [A-Z] [a-z]jest nieprawidłowy w prawie wszystkich lokalizacjach. na przykład w en-USustawieniach regionalnych A-Zjest faktycznie interwał AaBbCcDdEeFfGgHh...XxYyZ.
fuz

44

Wiem, że to stary post, ale podałem tę odpowiedź dla innej witryny, więc pomyślałem, że opublikuję ją tutaj:

GÓRNA -> dolna : użyj Pythona:

b=`echo "print '$a'.lower()" | python`

Lub Ruby:

b=`echo "print '$a'.downcase" | ruby`

Lub Perl (prawdopodobnie mój ulubiony):

b=`perl -e "print lc('$a');"`

Lub PHP:

b=`php -r "print strtolower('$a');"`

Lub Awk:

b=`echo "$a" | awk '{ print tolower($1) }'`

Lub Sed:

b=`echo "$a" | sed 's/./\L&/g'`

Lub Bash 4:

b=${a,,}

Lub NodeJS, jeśli go masz (i jesteś trochę szalony ...):

b=`echo "console.log('$a'.toLowerCase());" | node`

Możesz także użyć dd(ale nie chciałbym!):

b=`echo "$a" | dd  conv=lcase 2> /dev/null`

niższe -> GÓRNE :

użyj python:

b=`echo "print '$a'.upper()" | python`

Lub Ruby:

b=`echo "print '$a'.upcase" | ruby`

Lub Perl (prawdopodobnie mój ulubiony):

b=`perl -e "print uc('$a');"`

Lub PHP:

b=`php -r "print strtoupper('$a');"`

Lub Awk:

b=`echo "$a" | awk '{ print toupper($1) }'`

Lub Sed:

b=`echo "$a" | sed 's/./\U&/g'`

Lub Bash 4:

b=${a^^}

Lub NodeJS, jeśli go masz (i jesteś trochę szalony ...):

b=`echo "console.log('$a'.toUpperCase());" | node`

Możesz także użyć dd(ale nie chciałbym!):

b=`echo "$a" | dd  conv=ucase 2> /dev/null`

Także kiedy mówisz „shell”, zakładam, że masz na myśli, bashale jeśli możesz użyć, zshjest to tak proste jak

b=$a:l

dla małych liter i

b=$a:u

dla wielkich liter.


@JESii oba działają dla mnie górna -> dolna i dolna-> górna. Używam sed 4.2.2 i Bash 4.3.42 (1) na 64bit Debian Stretch.
nettux,

1
Cześć, @ nettux443 ... Właśnie próbowałem ponownie wykonać operację bash i nadal nie udaje mi się komunikat o błędzie „złe podstawienie”. Jestem na OSX, używając bash homebrew: GNU bash, wersja 4.3.42 (1) -release (x86_64-apple-darwin14.5.0)
JESii

5
Nie używaj! Wszystkie przykłady generujące skrypt są wyjątkowo kruche; jeśli wartość azawiera pojedynczy cytat, masz nie tylko zepsute zachowanie, ale poważny problem z bezpieczeństwem.
tripleee

Najbardziej podoba mi się rozwiązanie sed, ponieważ sed jest zawsze wszechobecny.
Dudi Boy,

Wolę używać rozwiązania dd. Pamiętaj, że musisz mieć
uprawnienia


18

Za pomocą GNU sed:

sed 's/.*/\L&/'

Przykład:

$ foo="Some STRIng";
$ foo=$(echo "$foo" | sed 's/.*/\L&/')
$ echo "$foo"
some string

12

Pre Bash 4.0

Bash Opuść wielkość liter ciągu i przypisz do zmiennej

VARIABLE=$(echo "$VARIABLE" | tr '[:upper:]' '[:lower:]') 

echo "$VARIABLE"

5
Nie ma potrzeby echoi rur: użyj$(tr '[:upper:]' '[:lower:]' <<<"$VARIABLE")
Tino

3
@Tino Ciąg tutaj nie jest przenośny z powrotem do naprawdę starych wersji Bash; Myślę, że został wprowadzony w wersji 3.
tripleee

1
@tripleee Masz rację, został wprowadzony w wersji bash-2.05b - jest to jednak najstarsza wersja bash, jaką udało mi się znaleźć w moich systemach
Tino

11

Dla standardowej powłoki (bez bashism) używającej tylko wbudowanych poleceń:

uppers=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lowers=abcdefghijklmnopqrstuvwxyz

lc(){ #usage: lc "SOME STRING" -> "some string"
    i=0
    while ([ $i -lt ${#1} ]) do
        CUR=${1:$i:1}
        case $uppers in
            *$CUR*)CUR=${uppers%$CUR*};OUTPUT="${OUTPUT}${lowers:${#CUR}:1}";;
            *)OUTPUT="${OUTPUT}$CUR";;
        esac
        i=$((i+1))
    done
    echo "${OUTPUT}"
}

I dla wielkich liter:

uc(){ #usage: uc "some string" -> "SOME STRING"
    i=0
    while ([ $i -lt ${#1} ]) do
        CUR=${1:$i:1}
        case $lowers in
            *$CUR*)CUR=${lowers%$CUR*};OUTPUT="${OUTPUT}${uppers:${#CUR}:1}";;
            *)OUTPUT="${OUTPUT}$CUR";;
        esac
        i=$((i+1))
    done
    echo "${OUTPUT}"
}

Zastanawiam się, czy nie pozwoliłeś na odrobinę szaleństwa w tym skrypcie, ponieważ nie jest on przenośny na FreeBSD sh: $ {1: $ ...}: Złe podstawienie
Dereckson

2
W rzeczy samej; podciągi z ${var:1:1}są Bashizmem.
tripleee

To podejście ma dość złe wskaźniki wydajności. Zobacz moją odpowiedź na dane.
Dejay Clayton

9

W bash 4 możesz użyć składu

Przykład:

A="HELLO WORLD"
typeset -l A=$A


7

Wyrażenie regularne

Chciałbym podziękować za polecenie, którym chcę się podzielić, ale prawda jest taka, że ​​uzyskałem je na własny użytek ze strony http://commandlinefu.com . Ma tę zaletę, że jeśli cdprzejdziesz do dowolnego katalogu w swoim własnym folderze domowym, to znaczy zmieni wszystkie pliki i foldery na małe litery rekurencyjnie, używaj ostrożnie. Jest to doskonała poprawka do wiersza poleceń i szczególnie przydatna w przypadku wielu albumów przechowywanych na dysku.

find . -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;

Możesz podać katalog zamiast kropki (.) Po znalezieniu, który oznacza bieżący katalog lub pełną ścieżkę.

Mam nadzieję, że to rozwiązanie okaże się użyteczne. Jedyną rzeczą, której nie robi to polecenie, jest zastąpienie spacji znakami podkreślenia - no cóż, może innym razem.


Nie działało to dla mnie z jakiegokolwiek powodu, choć wygląda dobrze. Udało mi się to jednak alternatywnie: znaleźć. -exec / bin / bash -c 'mv {} `tr [AZ] [az] <<< {}`' \;
John Rix

To wymaga prenameod perl: dpkg -S "$(readlink -e /usr/bin/rename)"dajeperl: /usr/bin/prename
Tino

4

Wiele odpowiedzi za pomocą zewnętrznych programów, które tak naprawdę nie są używane Bash.

Jeśli wiesz, że będziesz mieć dostępną wersję Bash4, powinieneś po prostu użyć ${VAR,,}notacji (jest to łatwe i fajne). W przypadku wersji Bash przed 4 (na moim komputerze Mac nadal jest używana wersja Bash 3.2). Użyłem poprawionej wersji odpowiedzi @ ghostdog74, aby stworzyć bardziej przenośną wersję.

Możesz zadzwonić lowercase 'my STRING'i uzyskać małą wersję. Czytam komentarze na temat ustawiania wyniku na var, ale to nie jest tak naprawdę przenośne Bash, ponieważ nie możemy zwrócić ciągów. Drukowanie to najlepsze rozwiązanie. Łatwy do uchwycenia za pomocą czegoś takiego var="$(lowercase $str)".

Jak to działa

Działa to poprzez uzyskanie reprezentacji liczb całkowitych ASCII każdego znaku za pomocą, printfa następnie adding 32if upper-to->lowerlub subtracting 32if lower-to->upper. Następnie użyj printfponownie, aby przekonwertować liczbę z powrotem na znak. Od 'A' -to-> 'a'mamy różnicę 32 znaków.

Za pomocą printfwyjaśnienia:

$ printf "%d\n" "'a"
97
$ printf "%d\n" "'A"
65

97 - 65 = 32

A to jest wersja robocza z przykładami.
Zwróć uwagę na komentarze w kodzie, ponieważ wyjaśniają wiele rzeczy:

#!/bin/bash

# lowerupper.sh

# Prints the lowercase version of a char
lowercaseChar(){
    case "$1" in
        [A-Z])
            n=$(printf "%d" "'$1")
            n=$((n+32))
            printf \\$(printf "%o" "$n")
            ;;
        *)
            printf "%s" "$1"
            ;;
    esac
}

# Prints the lowercase version of a sequence of strings
lowercase() {
    word="$@"
    for((i=0;i<${#word};i++)); do
        ch="${word:$i:1}"
        lowercaseChar "$ch"
    done
}

# Prints the uppercase version of a char
uppercaseChar(){
    case "$1" in
        [a-z])
            n=$(printf "%d" "'$1")
            n=$((n-32))
            printf \\$(printf "%o" "$n")
            ;;
        *)
            printf "%s" "$1"
            ;;
    esac
}

# Prints the uppercase version of a sequence of strings
uppercase() {
    word="$@"
    for((i=0;i<${#word};i++)); do
        ch="${word:$i:1}"
        uppercaseChar "$ch"
    done
}

# The functions will not add a new line, so use echo or
# append it if you want a new line after printing

# Printing stuff directly
lowercase "I AM the Walrus!"$'\n'
uppercase "I AM the Walrus!"$'\n'

echo "----------"

# Printing a var
str="A StRing WITH mixed sTUFF!"
lowercase "$str"$'\n'
uppercase "$str"$'\n'

echo "----------"

# Not quoting the var should also work, 
# since we use "$@" inside the functions
lowercase $str$'\n'
uppercase $str$'\n'

echo "----------"

# Assigning to a var
myLowerVar="$(lowercase $str)"
myUpperVar="$(uppercase $str)"
echo "myLowerVar: $myLowerVar"
echo "myUpperVar: $myUpperVar"

echo "----------"

# You can even do stuff like
if [[ 'option 2' = "$(lowercase 'OPTION 2')" ]]; then
    echo "Fine! All the same!"
else
    echo "Ops! Not the same!"
fi

exit 0

A wyniki po uruchomieniu tego:

$ ./lowerupper.sh 
i am the walrus!
I AM THE WALRUS!
----------
a string with mixed stuff!
A STRING WITH MIXED STUFF!
----------
a string with mixed stuff!
A STRING WITH MIXED STUFF!
----------
myLowerVar: a string with mixed stuff!
myUpperVar: A STRING WITH MIXED STUFF!
----------
Fine! All the same!

Powinno to jednak działać tylko w przypadku znaków ASCII .

Dla mnie jest w porządku, ponieważ wiem, że przekażę mu tylko znaki ASCII.
Używam tego na przykład do niektórych opcji CLI bez rozróżniania wielkości liter.


4

Konwersja jest wykonywana tylko dla alfabetów. Więc to powinno działać dobrze.

Koncentruję się na konwersji alfabetów między az z wielkich na małe litery. Wszelkie inne postacie powinny być po prostu wydrukowane w stdout, ponieważ jest ...

Konwertuje cały tekst w ścieżce / na / plik / nazwę pliku z zakresu az na AZ

Do konwersji małych i wielkich liter

cat path/to/file/filename | tr 'a-z' 'A-Z'

Do konwersji z wielkich na małe litery

cat path/to/file/filename | tr 'A-Z' 'a-z'

Na przykład,

Nazwa pliku:

my name is xyz

zostanie przekonwertowany na:

MY NAME IS XYZ

Przykład 2:

echo "my name is 123 karthik" | tr 'a-z' 'A-Z'
# Output:
# MY NAME IS 123 KARTHIK

Przykład 3:

echo "my name is 123 &&^&& #@$#@%%& kAR2~thik" | tr 'a-z' 'A-Z'
# Output:
# MY NAME IS 123 &&^&& #@0@%%& KAR2~THIK

3

Jeśli używasz v4, jest to włączone . Jeśli nie, oto proste, szeroko stosowane rozwiązanie. Inne odpowiedzi (i komentarze) w tym wątku były bardzo pomocne w tworzeniu poniższego kodu.

# Like echo, but converts to lowercase
echolcase () {
    tr [:upper:] [:lower:] <<< "${*}"
}

# Takes one arg by reference (var name) and makes it lowercase
lcase () { 
    eval "${1}"=\'$(echo ${!1//\'/"'\''"} | tr [:upper:] [:lower:] )\'
}

Uwagi:

  • Wykonanie: a="Hi All"a następnie: lcase azrobi to samo, co:a=$( echolcase "Hi All" )
  • W funkcji lcase użycie ${!1//\'/"'\''"}zamiast ${!1}pozwala na to, aby działało nawet wtedy, gdy ciąg zawiera cudzysłowy.

3

W przypadku wersji Bash wcześniejszych niż 4.0 ta wersja powinna być najszybsza (ponieważ nie rozwidla / nie wykonuje żadnych poleceń):

function string.monolithic.tolower
{
   local __word=$1
   local __len=${#__word}
   local __char
   local __octal
   local __decimal
   local __result

   for (( i=0; i<__len; i++ ))
   do
      __char=${__word:$i:1}
      case "$__char" in
         [A-Z] )
            printf -v __decimal '%d' "'$__char"
            printf -v __octal '%03o' $(( $__decimal ^ 0x20 ))
            printf -v __char \\$__octal
            ;;
      esac
      __result+="$__char"
   done
   REPLY="$__result"
}

Odpowiedź technozaura też miała potencjał, choć dla mnie działała poprawnie.


Nie jest zły! Aby przeanalizować skuteczność tego podejścia, zobacz moją odpowiedź dotyczącą wskaźników.
Dejay Clayton

3

Pomimo wieku tego pytania i podobnego do odpowiedzi udzielonej przez technozaura . Trudno mi było znaleźć rozwiązanie, które byłoby przenośne na większości platform (których używam), a także na starszych wersjach bash. Byłem również sfrustrowany tablicami, funkcjami i wykorzystaniem wydruków, echa i plików tymczasowych do wyszukiwania trywialnych zmiennych. Działa to dla mnie bardzo dobrze. Moje główne środowiska testowe to:

  1. GNU bash, wersja 4.1.2 (1) -release (x86_64-redhat-linux-gnu)
  2. GNU bash, wersja 3.2.57 (1) -release (sparc-sun-solaris2.10)
lcs="abcdefghijklmnopqrstuvwxyz"
ucs="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
input="Change Me To All Capitals"
for (( i=0; i<"${#input}"; i++ )) ; do :
    for (( j=0; j<"${#lcs}"; j++ )) ; do :
        if [[ "${input:$i:1}" == "${lcs:$j:1}" ]] ; then
            input="${input/${input:$i:1}/${ucs:$j:1}}" 
        fi
    done
done

Prosty styl C dla pętli do iteracji po łańcuchach. W poniższej linii, jeśli nie widziałeś czegoś takiego wcześniej, tutaj się tego nauczyłem . W tym przypadku wiersz sprawdza, czy znak $ {input: $ i: 1} (małe litery) istnieje w danych wejściowych, a jeśli tak, to zastępuje go danym znakiem $ {ucs: $ j: 1} (wielkie litery) i przechowuje go z powrotem do wejścia.

input="${input/${input:$i:1}/${ucs:$j:1}}"

Jest to niezwykle nieefektywne, zapętla 650 razy w powyższym przykładzie i zajmuje 35 sekund na wykonanie 1000 wywołań na mojej maszynie. Aby znaleźć alternatywę, która zapętla się tylko 11 razy i zajmuje mniej niż 5 sekund na wykonanie 1000 wywołań, zobacz moją alternatywną odpowiedź.
Dejay Clayton

1
Dzięki, chociaż powinno to być oczywiste po prostu patrząc na to. Być może błędy strony wynikają z wielkości wejściowej i liczby wykonywanych iteracji. Niemniej jednak podoba mi się twoje rozwiązanie.
JaredTS486,

3

Jest to znacznie szybsza odmiana podejścia JaredTS486, który wykorzystuje natywne możliwości Bash (w tym wersje Bash <4.0) do optymalizacji swojego podejścia.

Zmierzyłem czas 1000 iteracji tego podejścia dla małego ciągu (25 znaków) i większego ciągu (445 znaków), zarówno dla konwersji małych jak i wielkich liter. Ponieważ ciągi testowe są przeważnie pisane małymi literami, konwersje na małe litery są na ogół szybsze niż na wielkie.

Porównałem swoje podejście z kilkoma innymi odpowiedziami na tej stronie, które są kompatybilne z Bash 3.2. Moje podejście jest znacznie bardziej wydajne niż większość podejść tutaj udokumentowanych i jest nawet szybsze niż trw kilku przypadkach.

Oto wyniki synchronizacji dla 1000 iteracji po 25 znaków:

Wyniki czasowe dla 1000 iteracji 445 znaków (składających się z wiersza „Robin” Wittera Bynnera):

  • 2 za moje podejście do małych liter; 12s dla wielkich liter
  • 4 s na trmałe litery; 4s dla wielkich liter
  • 20s za podejście Orwellophile do małych liter; 29s na wielkie litery
  • 75s za podejście ghostdog74 do małych liter; 669s dla wielkich liter. Warto zauważyć, jak dramatyczna jest różnica w wydajności między testem z dominującymi meczami a testem z dominującymi chybcami
  • 467s za podejście technozaura do małych liter; 449s dla wielkich liter
  • 660 dla podejścia JaredTS486 do małych liter; 660s na wielkie litery. Warto zauważyć, że takie podejście generowało ciągłe błędy stron (zamiana pamięci) w Bash

Rozwiązanie:

#!/bin/bash
set -e
set -u

declare LCS="abcdefghijklmnopqrstuvwxyz"
declare UCS="ABCDEFGHIJKLMNOPQRSTUVWXYZ"

function lcase()
{
  local TARGET="${1-}"
  local UCHAR=''
  local UOFFSET=''

  while [[ "${TARGET}" =~ ([A-Z]) ]]
  do
    UCHAR="${BASH_REMATCH[1]}"
    UOFFSET="${UCS%%${UCHAR}*}"
    TARGET="${TARGET//${UCHAR}/${LCS:${#UOFFSET}:1}}"
  done

  echo -n "${TARGET}"
}

function ucase()
{
  local TARGET="${1-}"
  local LCHAR=''
  local LOFFSET=''

  while [[ "${TARGET}" =~ ([a-z]) ]]
  do
    LCHAR="${BASH_REMATCH[1]}"
    LOFFSET="${LCS%%${LCHAR}*}"
    TARGET="${TARGET//${LCHAR}/${UCS:${#LOFFSET}:1}}"
  done

  echo -n "${TARGET}"
}

Podejście jest proste: podczas gdy w ciągu wejściowym znajdują się pozostałe wielkie litery, znajdź następną i zamień wszystkie wystąpienia tej litery na jej małą literę. Powtarzaj, aż wszystkie wielkie litery zostaną zastąpione.

Niektóre cechy wydajności mojego rozwiązania:

  1. Używa tylko wbudowanych narzędzi powłoki, co pozwala uniknąć nakładania się na zewnętrzne narzędzia binarne w nowym procesie
  2. Unika podpowłok, które podlegają karom za wydajność
  3. Korzysta z mechanizmów powłoki, które zostały skompilowane i zoptymalizowane pod kątem wydajności, takich jak globalne zastępowanie ciągów zmiennych, przycinanie sufiksu zmiennych oraz wyszukiwanie i dopasowanie wyrażeń regularnych. Mechanizmy te są znacznie szybsze niż ręczne iterowanie po łańcuchach
  4. Pętle tylko tyle razy, ile wymaga liczba unikatowych pasujących znaków do konwersji. Na przykład konwersja łańcucha zawierającego trzy różne wielkie litery na małe wymaga tylko 3 iteracji pętli. W przypadku wstępnie skonfigurowanego alfabetu ASCII maksymalna liczba iteracji pętli wynosi 26
  5. UCSi LCSmoże być uzupełniony o dodatkowe postacie

2

Aby zapisać przekształcony ciąg w zmienną. Następujące pracował dla mnie - $SOURCE_NAMEdo$TARGET_NAME

TARGET_NAME="`echo $SOURCE_NAME | tr '[:upper:]' '[:lower:]'`"

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.