Как преобразовать строку в нижний регистр в Bash?

810

Есть ли способ в преобразовать строку в строчную строку?

Например, если у меня есть:

a="Hi all"

Я хочу преобразовать его в:

"hi all"
Теги:
string

17 ответов

1319
Лучший ответ

Существуют различные способы:

tr

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

AWK

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

Bash 4.0

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

Perl

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

Bash

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
  • 8
    Я что-то упустил, или ваш последний пример (в Bash) действительно делает что-то совершенно другое? Это работает для "ABX", но если вместо этого сделать word="Hi All" как в других примерах, возвращается ha , а не hi all . Он работает только для заглавных букв и пропускает буквы в нижнем регистре.
  • 25
    Обратите внимание, что в стандарте POSIX указаны только примеры tr и awk .
Показать ещё 22 комментария
295

В Bash 4:

В нижнем регистре

$ 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

В верхний регистр

$ 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

Переключить (недокументированный, но необязательно настраиваемый во время компиляции)

$ 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

Заглавная (недокументированная, но необязательно конфигурируемая во время компиляции)

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

Случай с заголовком:

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

Чтобы отключить атрибут declare, используйте +. Например, declare +c string. Это влияет на последующие назначения, а не на текущее значение.

Параметры declare изменяют атрибут переменной, но не содержимое. Перераспределения в моих примерах обновляют содержимое, чтобы показать изменения.

Edit:

Добавлен "переключить первый символ по слову" (${var~}), как предлагается ghostdog74.

Изменить: Исправлено поведение тильды в соответствии с Bash 4.3.

  • 5
    есть также ${string~}
  • 5
    Довольно странно, операторы «^^» и «,,» не работают с не-ASCII-символами, но «~~» работает ... So string="łódź"; echo ${string~~} вернет "ŁÓDŹ", но echo ${string^^} вернет "łóDź". Даже в LC_ALL=pl_PL.utf-8 . Это использует Bash 4.2.24.
Показать ещё 10 комментариев
108
echo "Hi All" | tr "[:upper:]" "[:lower:]"
  • 2
    +1 за не английский
  • 4
    @RichardHansen: tr не работает для меня для не-ACII символов. У меня есть правильный набор локали и сгенерированные файлы локали. Есть идеи, что я могу делать не так?
Показать ещё 1 комментарий
65

tr:

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

AWK:

{ print tolower($0) }

sed:

y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
  • 2
    +1 a="$(tr [AZ] [az] <<< "$a")" выглядит мне проще всего. Я все еще начинающий ...
  • 1
    Я настоятельно рекомендую решение sed ; Я работал в среде, которая по какой-то причине не имеет tr но мне еще не удалось найти систему без sed , плюс большую часть времени я хочу сделать это, я все равно просто сделал что-то еще в sed поэтому можно объединить команды в один (длинный) оператор.
Показать ещё 6 комментариев
30

Я знаю, что это старая должность, но я сделал этот ответ для другого сайта, поэтому я подумал, что разместил его здесь:

ВЕРХНИЙ → нижний: используйте python:

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

Или Ruby:

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

Или Perl (возможно, мой любимый):

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

Или PHP:

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

Или Awk:

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

Или Sed:

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

Или Bash 4:

b=${a,,}

Или NodeJS, если у вас есть (и немного орехи...):

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

Вы также можете использовать dd (но я бы не стал!):

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

ниже → ВЕРХНИЙ:

использовать python:

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

Или Ruby:

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

Или Perl (возможно, мой любимый):

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

Или PHP:

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

Или Awk:

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

Или Sed:

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

Или Bash 4:

b=${a^^}

Или NodeJS, если у вас есть (и немного орехи...):

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

Вы также можете использовать dd (но я бы не стал!):

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

Также, когда вы говорите "оболочка", я предполагаю, что вы имеете в виду bash, но если вы можете использовать zsh, это так же просто, как

b=$a:l

для нижнего регистра и

b=$a:u

для верхнего регистра.

  • 2
    Ни команда sed, ни команда bash не работали для меня.
  • 0
    @JESii оба работают для меня верхний -> нижний и нижний -> верхний. Я использую sed 4.2.2 и Bash 4.3.42 (1) на 64-битной Debian Stretch.
Показать ещё 4 комментария
22

В zsh:

echo $a:u

Надо любить zsh!

  • 3
    или $ a: l для преобразования в нижний регистр
  • 0
    Добавьте еще один случай: echo ${(C)a} #Upcase the first char only
15

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

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

Пример:

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

Для стандартной оболочки (без багизмов), использующей только встроенные функции:

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

И для верхнего регистра:

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}"
}
  • 0
    Интересно, не допустили ли вы некоторого башизма в этом сценарии, поскольку он не переносим во FreeBSD sh: $ {1: $ ...}: плохая замена
  • 2
    В самом деле; подстроки с ${var:1:1} являются башизмом.
Показать ещё 1 комментарий
7

Pre Bash 4.0

Bash Опустить случай строки и назначить переменной

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

echo "$VARIABLE"
  • 4
    Нет необходимости в echo и каналах: используйте $(tr '[:upper:]' '[:lower:]' <<<"$VARIABLE")
  • 2
    @Tino Строка here также не переносима на действительно старые версии Bash; Я считаю, что это было введено в v3.
Показать ещё 1 комментарий
7

В bash 4 вы можете использовать набор

Пример:

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

Регулярное выражение

Я хотел бы взять на себя ответственность за команду, которую хочу поделиться, но правда в том, что я получил ее для собственного использования из http://commandlinefu.com, Преимущество состоит в том, что если вы cd в любой каталог в своей домашней папке, то это изменит все файлы и папки на нижний регистр рекурсивно, пожалуйста, используйте с осторожностью. Это блестящее исправление командной строки и особенно полезно для тех множества альбомов, которые вы сохранили на своем диске.

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

Вы можете указать каталог вместо точки (.) после поиска, которая обозначает текущий каталог или полный путь.

Надеюсь, что это решение окажется полезным тем, что эта команда не делает, это заменить пробелы символами подчеркивания - возможно, еще раз.

  • 2
    спасибо за commandlinefu.com
  • 0
    Это не сработало для меня по любой причине, хотя выглядит хорошо. Я сделал это работать в качестве альтернативы: найти. -exec / bin / bash -c 'mv {} `tr [AZ] [az] <<< {}`' \;
Показать ещё 1 комментарий
4

Вы можете попробовать это

s="Hello World!" 

echo $s  # Hello World!

a=${s,,}
echo $a  # hello world!

b=${s^^}
echo $b  # HELLO WORLD!

Изображение 3880

ref: http://wiki.workassis.com/shell-script-convert-text-to-lowercase-and-uppercase/

3

Для версий Bash, предшествующих 4.0, эта версия должна быть самой быстрой (так как она не выполняет fork/exec любые команды):

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

ответ technosaurus также имел потенциал, хотя он действительно работал правильно для меня.

  • 0
    Неплохо! Для анализа эффективности этого подхода, пожалуйста, смотрите мой ответ для метрик.
2

Многие ответы используют внешние программы, которые на самом деле не используют Bash.

Если вы знаете, что у вас будет доступный Bash4, вы должны просто использовать нотацию ${VAR,,} (это легко и просто). Для Bash до 4 (например, для моего Mac по-прежнему используется Bash 3.2). Я использовал исправленную версию ответа @ghostdog74 для создания более переносимой версии.

Вы можете вызвать lowercase 'my STRING' и получить версию в нижнем регистре. Я читал комментарии об установке результата в var, но это не очень переносимо в Bash, так как мы не можем возвращать строки. Печать - лучшее решение. Легко захватывать с чем-то вроде var="$(lowercase $str)".

Как это работает

Как это работает, получая целочисленное представление ASCII каждого char с printf, а затем adding 32, если upper-to->lower, или subtracting 32, если lower-to->upper. Затем снова используйте printf, чтобы преобразовать число обратно в char. Из 'A' -to-> 'a' мы имеем разницу в 32 символа.

Используя printf, чтобы объяснить:

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

97 - 65 = 32

И это рабочая версия с примерами.
Обратите внимание на комментарии в коде, так как они объясняют многое:

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

И результаты после запуска:

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

Это должно работать только для символов ASCII, но.

Для меня это прекрасно, так как я знаю, что буду передавать только ASCII-символы.
Я использую это для некоторых нечувствительных к регистру параметров CLI, например.

2

Несмотря на то, насколько старый этот вопрос и похож на этот ответ technosaurus. Мне было трудно найти решение, которое было переносимым на большинстве платформ (это я использую), а также более старые версии bash. Я также был расстроен массивами, функциями и использованием отпечатков, эхо и временных файлов для извлечения тривиальных переменных. Это очень хорошо для меня, я думал, что поделюсь. Моей основной средой тестирования являются:

  • GNU bash, версия 4.1.2 (1) -release (x86_64-redhat-linux-gnu)
  • GNU bash, версия 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

Простой C-стиль для цикла для итерации по строкам. Для строки ниже, если вы не видели ничего подобного раньше здесь я узнал об этом. В этом случае строка проверяет, существует ли char ${input: $i: 1} (строчный регистр) во входном файле, и если он заменяет его заданным char ${ucs: $j: 1} (верхний регистр ) и сохраняет его обратно на вход.

input="${input/${input:$i:1}/${ucs:$j:1}}"
  • 0
    Это крайне неэффективно, в вашем примере выше 650 циклов и 35 секунд для выполнения 1000 вызовов на моем компьютере. Для альтернативы, которая повторяется всего 11 раз и требует менее 5 секунд для выполнения 1000 вызовов, см. Мой альтернативный ответ.
  • 1
    Спасибо, хотя это должно быть очевидно, просто глядя на это. Возможно, ошибки страницы связаны с размером ввода и количеством выполняемых итераций. Тем не менее мне нравится ваше решение.
2

При использовании v4 это запеченный. Если нет, вот простое, широко применимое решение. Другие ответы (и комментарии) по этой теме были весьма полезны при создании кода ниже.

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

Примечания:

  • Выполнение: a="Hi All", а затем: lcase a будет делать то же самое, что: a=$( echolcase "Hi All" )
  • В lcase-функции использование ${!1//\'/"'\''"} вместо ${!1} позволяет работать, даже если строка содержит кавычки.
0

Сохранить преобразованную строку в переменную. После работы для меня - $SOURCE_NAME до $TARGET_NAME

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню