Comment convertir une chaîne en minuscules dans Bash?

Réponses:

2182

Il existe différentes manières:

Norme POSIX

tr

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

AWK

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

Non-POSIX

Vous pouvez rencontrer des problèmes de portabilité avec les exemples suivants:

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

Frapper

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

Remarque: YMMV sur celui-ci. Ne fonctionne pas pour moi (GNU bash version 4.2.46 et 4.0.33 (et même comportement 2.05b.0 mais nocasematch n'est pas implémenté)) même avec using shopt -u nocasematch;. Ne pas définir que nocasematch fait que [["fooBaR" == "FOObar"]] correspond à OK MAIS étrangement à l'intérieur de la casse [bz] est incorrectement mis en correspondance par [AZ]. Bash est confondu par le double négatif ("unsetting nocasematch")! :-)

ghostdog74
la source
9
Suis-je en train de manquer quelque chose, ou votre dernier exemple (dans Bash) fait-il quelque chose de complètement différent? Cela fonctionne pour "ABX", mais si vous faites word="Hi All"comme les autres exemples, il revient ha, non hi all. Il ne fonctionne que pour les lettres majuscules et ignore les lettres déjà en minuscules.
jangosteve
26
Notez que seuls les exemples tret awksont spécifiés dans la norme POSIX.
Richard Hansen
178
tr '[:upper:]' '[:lower:]'utilisera les paramètres régionaux actuels pour déterminer les équivalents majuscules / minuscules, donc cela fonctionnera avec les paramètres régionaux qui utilisent des lettres avec des signes diacritiques.
Richard Hansen
10
Comment obtient-on la sortie dans une nouvelle variable? C'est-à-dire que je veux la chaîne en minuscule dans une nouvelle variable?
Adam Parkin
60
@Adam:b="$(echo $a | tr '[A-Z]' '[a-z]')"
Tino
435

Dans Bash 4:

Pour minuscule

$ 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

En majuscules

$ 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

Toggle (non documenté, mais configurable en option au moment de la compilation)

$ 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

Capitaliser (non documenté, mais configurable en option au moment de la compilation)

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

Cas de titre:

$ 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

Pour désactiver un declareattribut, utilisez +. Par exemple declare +c string,. Cela affecte les affectations suivantes et non la valeur actuelle.

Les declareoptions modifient l'attribut de la variable, mais pas le contenu. Les réaffectations dans mes exemples mettent à jour le contenu pour montrer les changements.

Éditer:

Ajout de "basculer le premier caractère par mot" ( ${var~}) comme suggéré par ghostdog74 .

Edit: comportement tilde corrigé pour correspondre à Bash 4.3.

En pause jusqu'à nouvel ordre.
la source
5
Assez bizzare, les opérateurs "^^" et ",," ne fonctionnent pas sur les caractères non ASCII mais "~~" le fait ... Donc string="łódź"; echo ${string~~}, retournera "ŁÓDŹ", mais echo ${string^^}renverra "łóDź". Même en LC_ALL=pl_PL.utf-8. Cela utilise bash 4.2.24.
Hubert Kario
2
@HubertKario: C'est bizarre. C'est la même chose pour moi dans Bash 4.0.33 avec la même chaîne en_US.UTF-8. C'est un bug et je l'ai signalé.
pause jusqu'à nouvel ordre.
1
@HubertKario: Essayez echo "$string" | tr '[:lower:]' '[:upper:]'. Il présentera probablement le même échec. Le problème n'est donc pas au moins en partie celui de Bash.
pause jusqu'à nouvel ordre.
1
@ DennisWilliamson: Oui, je l'ai remarqué aussi (voir le commentaire de la réponse Shuvalov). Je dirais juste, "ce truc est seulement pour ASCII", mais c'est l'opérateur "~~" qui fonctionne, donc ce n'est pas comme si le code et les tables de traduction n'étaient pas déjà là ...
Hubert Kario
4
@HubertKario: Le mainteneur de Bash a reconnu le bogue et a déclaré qu'il sera corrigé dans la prochaine version.
pause jusqu'à nouvel ordre.
123
echo "Hi All" | tr "[:upper:]" "[:lower:]"
shuvalov
la source
4
@RichardHansen: trne fonctionne pas pour moi pour les personnages non-ACII. J'ai un ensemble de paramètres régionaux correct et des fichiers de paramètres régionaux générés. Avez-vous une idée de ce que je pourrais faire de mal?
Hubert Kario
FYI: Cela a fonctionné sur Windows / Msys. Certaines autres suggestions ne l'ont pas fait.
wasatchwizard
3
Pourquoi est-il [:upper:]nécessaire?
mgutt
77

tr :

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

AWK :

{ print tolower($0) }

sed :

y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
Ignacio Vazquez-Abrams
la source
2
+1 a="$(tr [A-Z] [a-z] <<< "$a")"me semble le plus simple. Je suis toujours un débutant ...
Sandeepan Nath
2
Je recommande fortement la sedsolution; J'ai travaillé dans un environnement qui, pour une raison quelconque, n'en a pas, trmais je n'ai pas encore trouvé de système sans sed, et la plupart du temps je veux le faire, je viens de faire quelque chose d'autre de sedtoute façon, donc je peux enchaîner les commandes ensemble dans une seule instruction (longue).
Haravikk
2
Les expressions entre crochets doivent être citées. Dans tr [A-Z] [a-z] A, le shell peut effectuer une expansion du nom de fichier s'il existe des noms de fichiers composés d'une seule lettre ou si nullgob est défini. tr "[A-Z]" "[a-z]" Ase comportera correctement.
Dennis
2
@CamiloMartin, c'est un système BusyBox où j'ai ce problème, en particulier les Synology NAS, mais je l'ai rencontré sur quelques autres systèmes également. J'ai fait beaucoup de scripts shell multiplateforme récemment, et avec l'exigence que rien de plus ne soit installé, cela rend les choses très délicates! Cependant, je n'ai pas encore rencontré de système sanssed
Haravikk
2
Notez que tr [A-Z] [a-z]c'est incorrect dans presque tous les environnements locaux. par exemple, dans les en-USparamètres régionaux, A-Zest en fait l'intervalle AaBbCcDdEeFfGgHh...XxYyZ.
fuz
44

Je sais que c'est un article ancien mais j'ai fait cette réponse pour un autre site, alors j'ai pensé le publier ici:

UPPER -> lower : utilisez python:

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

Ou Ruby:

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

Ou Perl (probablement mon préféré):

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

Ou PHP:

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

Ou Awk:

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

Ou Sed:

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

Ou Bash 4:

b=${a,,}

Ou NodeJS si vous l'avez (et êtes un peu fou ...):

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

Vous pouvez également utiliser dd(mais je ne le ferais pas!):

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

inférieur -> SUPÉRIEUR :

utilisez python:

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

Ou Ruby:

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

Ou Perl (probablement mon préféré):

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

Ou PHP:

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

Ou Awk:

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

Ou Sed:

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

Ou Bash 4:

b=${a^^}

Ou NodeJS si vous l'avez (et êtes un peu fou ...):

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

Vous pouvez également utiliser dd(mais je ne le ferais pas!):

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

Aussi quand vous dites «shell», je suppose que vous voulez dire, bashmais si vous pouvez l'utiliser, zshc'est aussi simple que

b=$a:l

pour les minuscules et

b=$a:u

pour les majuscules.

nettux
la source
@JESii fonctionne pour moi en haut -> en bas et en bas -> en haut. J'utilise sed 4.2.2 et Bash 4.3.42 (1) sur Debian Stretch 64 bits.
nettux
1
Salut, @ nettux443 ... Je viens de réessayer l'opération bash et elle échoue toujours pour moi avec le message d'erreur "mauvaise substitution". Je suis sur OSX en utilisant bash de homebrew: GNU bash, version 4.3.42 (1) -release (x86_64-apple-darwin14.5.0)
JESii
5
Ne pas utiliser! Tous les exemples qui génèrent un script sont extrêmement fragiles; si la valeur de acontient une seule citation, vous avez non seulement un comportement défectueux, mais un grave problème de sécurité.
tripleee
J'aime le plus la solution sed, car sed est toujours omniprésente.
Dudi Boy
Je préfère utiliser la solution dd. Veuillez noter que vous devez être root pour le faire fonctionner
inetphantom
29

En zsh:

echo $a:u

Je dois aimer zsh!

Scott Smedley
la source
4
ou $ a: l pour la conversion en minuscules
Scott Smedley
1
Ajouter un autre cas:echo ${(C)a} #Upcase the first char only
biocyberman
18

Utilisation de GNU sed:

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

Exemple:

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

Pre Bash 4.0

Bash Abaissez la casse d'une chaîne et affectez-la à une variable

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

echo "$VARIABLE"
hawkeye126
la source
5
Pas besoin de echotuyaux: utilisez$(tr '[:upper:]' '[:lower:]' <<<"$VARIABLE")
Tino
3
@Tino La chaîne here n'est pas non plus transférable aux versions très anciennes de Bash; Je crois qu'il a été introduit dans la v3.
tripleee
1
@tripleee Vous avez raison, il a été introduit dans bash-2.05b - mais c'est le plus ancien bash que j'ai pu trouver sur mes systèmes
Tino
11

Pour un shell standard (sans bashismes) utilisant uniquement des builtins:

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}"
}

Et pour les majuscules:

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}"
}
technosaurus
la source
Je me demande si vous n'avez pas laissé de bashisme dans ce script, car il n'est pas portable sur FreeBSD sh: $ {1: $ ...}: Mauvaise substitution
Dereckson
2
En effet; les sous-chaînes avec ${var:1:1}sont un bashisme.
tripleee
Cette approche a de très mauvaises mesures de performances. Voir ma réponse pour les métriques.
Dejay Clayton
9

Dans bash 4, vous pouvez utiliser typeset

Exemple:

A="HELLO WORLD"
typeset -l A=$A
c4f4t0r
la source
7

Expression régulière

Je voudrais prendre le crédit de la commande que je souhaite partager, mais la vérité est que je l'ai obtenue pour mon propre usage sur http://commandlinefu.com . Il a l'avantage que si vous accédez cdà n'importe quel répertoire de votre propre dossier de départ, cela changera tous les fichiers et dossiers en minuscules de manière récursive, veuillez les utiliser avec prudence. C'est une solution brillante en ligne de commande et particulièrement utile pour les multitudes d'albums que vous avez stockés sur votre disque.

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

Vous pouvez spécifier un répertoire à la place du point (.) Après la recherche qui indique le répertoire actuel ou le chemin complet.

J'espère que cette solution s'avère utile la seule chose que cette commande ne fait pas est de remplacer les espaces par des traits de soulignement - eh bien une autre fois peut-être.

Derek Shaw
la source
Cela n'a pas fonctionné pour moi pour une raison quelconque, bien que cela semble bien. J'ai réussi à faire en sorte que cela fonctionne comme une alternative: trouver. -exec / bin / bash -c 'mv {} `tr [AZ] [az] <<< {}`' \;
John Rix
Cela nécessite prenamede perl: dpkg -S "$(readlink -e /usr/bin/rename)"donneperl: /usr/bin/prename
Tino
4

Beaucoup de réponses en utilisant des programmes externes, ce qui n'est pas vraiment utile Bash.

Si vous savez que Bash4 sera disponible, vous devriez vraiment utiliser la ${VAR,,}notation (c'est facile et cool). Pour Bash avant 4 (mon Mac utilise toujours Bash 3.2 par exemple). J'ai utilisé la version corrigée de la réponse de @ ghostdog74 pour créer une version plus portable.

Celui que vous pouvez appeler lowercase 'my STRING'et obtenir une version en minuscules. J'ai lu des commentaires sur la définition du résultat sur une variable, mais ce n'est pas vraiment portable Bash, car nous ne pouvons pas retourner de chaînes. L'imprimer est la meilleure solution. Facile à capturer avec quelque chose comme var="$(lowercase $str)".

Comment ça marche

La façon dont cela fonctionne consiste à obtenir la représentation entière ASCII de chaque caractère avec printfpuis adding 32si upper-to->lower, ou subtracting 32si lower-to->upper. Ensuite, utilisez à printfnouveau pour reconvertir le nombre en caractère. De 'A' -to-> 'a'nous avons une différence de 32 caractères.

Utiliser printfpour expliquer:

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

97 - 65 = 32

Et ceci est la version de travail avec des exemples.
Veuillez noter les commentaires dans le code, car ils expliquent beaucoup de choses:

#!/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

Et les résultats après avoir exécuté ceci:

$ ./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!

Cela ne devrait cependant fonctionner que pour les caractères ASCII .

Pour moi, c'est bien, car je sais que je ne lui transmettrai que des caractères ASCII.
Je l'utilise pour certaines options CLI insensibles à la casse, par exemple.

Gus Neves
la source
4

La conversion de la casse est effectuée uniquement pour les alphabets. Donc, cela devrait fonctionner parfaitement.

Je me concentre sur la conversion des alphabets entre az de majuscules en minuscules. Tout autre caractère doit simplement être imprimé dans stdout tel quel ...

Convertit tout le texte du chemin / vers / fichier / nom de fichier dans la plage az en AZ

Pour convertir des minuscules en majuscules

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

Pour la conversion de majuscules en minuscules

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

Par exemple,

nom de fichier:

my name is xyz

est converti en:

MY NAME IS XYZ

Exemple 2:

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

Exemple 3:

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

Si vous utilisez v4, cela est intégré . Sinon, voici une solution simple et largement applicable . D'autres réponses (et commentaires) sur ce fil ont été très utiles pour créer le code ci-dessous.

# 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:] )\'
}

Remarques:

  • Faire: a="Hi All"et ensuite: lcase afera la même chose que:a=$( echolcase "Hi All" )
  • Dans la fonction lcase, l'utilisation de ${!1//\'/"'\''"}au lieu de ${!1}permet à cela de fonctionner même lorsque la chaîne a des guillemets.
Stephen M. Harris
la source
3

Pour les versions de Bash antérieures à 4.0, cette version devrait être la plus rapide (car elle ne fork / n'exécute aucune commande):

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"
}

La réponse de technosaurus avait aussi du potentiel, même si elle fonctionnait correctement pour moi.

Orwellophile
la source
Pas mal! Pour une analyse des performances de cette approche, veuillez consulter ma réponse pour les mesures.
Dejay Clayton
3

Malgré l'âge de cette question et similaire à cette réponse du technosaurus . J'ai eu du mal à trouver une solution portable sur la plupart des plates-formes (que j'utilise) ainsi que sur les anciennes versions de bash. J'ai également été frustré par les tableaux, les fonctions et l'utilisation des impressions, des échos et des fichiers temporaires pour récupérer des variables triviales. Cela fonctionne très bien pour moi jusqu'à présent, je pensais que je partagerais. Mes principaux environnements de test sont:

  1. GNU bash, version 4.1.2 (1) -release (x86_64-redhat-linux-gnu)
  2. GNU bash, version 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

Style C simple pour une boucle pour parcourir les cordes. Pour la ligne ci-dessous, si vous n'avez jamais rien vu de tel auparavant, c'est là que j'ai appris cela . Dans ce cas, la ligne vérifie si le caractère $ {input: $ i: 1} (minuscule) existe en entrée et si c'est le cas le remplace par le caractère donné $ {ucs: $ j: 1} (majuscule) et le stocke en entrée.

input="${input/${input:$i:1}/${ucs:$j:1}}"
JaredTS486
la source
Ceci est extrêmement inefficace, bouclant 650 fois dans votre exemple ci-dessus et prenant 35 secondes pour exécuter 1000 invocations sur ma machine. Pour une alternative qui boucle seulement 11 fois et prend moins de 5 secondes pour exécuter 1000 invocations, voir ma réponse alternative.
Dejay Clayton
1
Merci, bien que cela devrait être évident juste en le regardant. Les défauts de page sont peut-être dus à la taille d'entrée et au nombre d'itérations que vous exécutez. Néanmoins, j'aime votre solution.
JaredTS486
3

Il s'agit d'une variante beaucoup plus rapide de l'approche de JaredTS486 qui utilise les capacités natives de Bash (y compris les versions Bash <4.0) pour optimiser son approche.

J'ai chronométré 1000 itérations de cette approche pour une petite chaîne (25 caractères) et une chaîne plus grande (445 caractères), à la fois pour les conversions en minuscules et en majuscules. Étant donné que les chaînes de test sont principalement en minuscules, les conversions en minuscules sont généralement plus rapides qu'en majuscules.

J'ai comparé mon approche avec plusieurs autres réponses sur cette page qui sont compatibles avec Bash 3.2. Mon approche est beaucoup plus performante que la plupart des approches documentées ici, et est encore plus rapide que trdans plusieurs cas.

Voici les résultats de chronométrage pour 1000 itérations de 25 caractères:

Résultats de chronométrage pour 1000 itérations de 445 caractères (composé du poème "Le Robin" de Witter Bynner):

  • 2s pour mon approche des minuscules; 12 s pour les majuscules
  • 4s pour trminuscule; 4s pour les majuscules
  • 20s pour l'approche d' Orwellophile en minuscules; 29s pour les majuscules
  • 75s pour 75s l' approche de ghostdog74 en minuscules; 669 pour les majuscules. Il est intéressant de noter à quel point la différence de performances est spectaculaire entre un test avec des correspondances prédominantes et un test avec des ratés prédominants
  • 467 pour l'approche du technosaurus en minuscules; 449s pour les majuscules
  • 660 pour l'approche de JaredTS486 en minuscules; 660 pour les majuscules. Il est intéressant de noter que cette approche a généré des défauts de page continus (échange de mémoire) dans Bash

Solution:

#!/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}"
}

L'approche est simple: alors que la chaîne d'entrée contient toutes les lettres majuscules restantes, recherchez la suivante et remplacez toutes les instances de cette lettre par sa variante minuscule. Répétez jusqu'à ce que toutes les lettres majuscules soient remplacées.

Quelques caractéristiques de performance de ma solution:

  1. Utilise uniquement des utilitaires intégrés au shell, ce qui évite la surcharge d'invoquer des utilitaires binaires externes dans un nouveau processus
  2. Évite les sous-coques, qui entraînent des pénalités de performance
  3. Utilise des mécanismes de shell qui sont compilés et optimisés pour les performances, tels que le remplacement global de chaînes dans les variables, le découpage des suffixes variables et la recherche et la correspondance d'expressions régulières. Ces mécanismes sont beaucoup plus rapides que l'itération manuelle via des chaînes
  4. Boucle uniquement le nombre de fois requis par le nombre de caractères correspondants uniques à convertir. Par exemple, la conversion d'une chaîne contenant trois caractères majuscules différents en minuscules ne nécessite que 3 itérations de boucle. Pour l'alphabet ASCII préconfiguré, le nombre maximal d'itérations de boucle est de 26
  5. UCSet LCSpeut être augmenté de caractères supplémentaires
Dejay Clayton
la source
2

Pour stocker la chaîne transformée dans une variable. À la suite travaillé pour moi - $SOURCE_NAMEà$TARGET_NAME

TARGET_NAME="`echo $SOURCE_NAME | tr '[:upper:]' '[:lower:]'`"
nitinr708
la source
1

Manière simple

echo "Hi all" | awk '{ print tolower($0); }'
Atul23
la source