it-roy-ru.com

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

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

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

a="Hi all"

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

"hi all"
1008
assassin

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

POSIX стандарт

tr

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

AWK

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

Non-POSIX

Вы можете столкнуться с проблемами переносимости в следующих примерах:

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

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
1779
ghostdog74

В 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"
A few words

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

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

Правка:

Добавлено «переключение первого символа с помощью Word» (${var~}), как предложено ghostdog74 .

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

383
Dennis Williamson
echo "Hi All" | tr "[:upper:]" "[:lower:]"
116
shuvalov

tr :

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

AWK :

{ print tolower($0) }

sed :

y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
72
Ignacio Vazquez-Abrams

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

UPPER -> Lower : Использовать python:

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

Или рубин

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) }'`

Или сед:

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

Или Баш 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`

Или рубин

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) }'`

Или сед:

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

Или Баш 4:

b=${a^^}

Или NodeJS, если он у вас есть (и немного чокнутый ...):

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

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

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

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

b=$a:l

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

b=$a:u

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

38
nettux

В зш:

echo $a:u

Должен любить Zsh!

26
Scott Smedley

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

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

Пример:

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

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

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

Pre Bash 4.0

Bash Нижний регистр строки и присвоение переменной

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

echo "$VARIABLE"
10
hawkeye126

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

Пример:

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

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

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

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

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

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

7
Derek Shaw

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

s="Hello World!" 

echo $s  # Hello World!

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

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

 enter image description here

ссылка: http://wiki.workassis.com/Shell-script-convert-text-to-lowercase-and-uppercase/

6
Bikesh M Annur

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

  1. GNU bash, версия 4.1.2 (1) -релиз (x86_64-redhat-linux-gnu) 
  2. GNU bash, версия 3.2.57 (1) -релиз (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-стиль для цикла для итерации по строкам . Для строки ниже, если вы еще не видели ничего подобного это то, где я узнал это . В этом случае строка проверяет, существует ли на входе символ $ {input: $ i: 1} (нижний регистр) и, если это так, заменяет его на заданный символ $ {ucs: $ j: 1} (верхний регистр) и сохраняет его вернуться на вход.

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

Для версий 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"
}

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

3
Orwellophile

Если используется 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} позволяет работать, даже если строка содержит кавычки.
3
Stephen M. Harris

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

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

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

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

Это работает путем получения целочисленного представления ASCII каждого символа с помощью printf, а затем adding 32, если upper-to->lower, или subtracting 32, если lower-to->upper. Затем снова используйте printf, чтобы преобразовать число обратно в символ. От '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="[email protected]"
    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="[email protected]"
    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 "[email protected]" 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 без учета регистра, например.

3
Gus Neves

Если вам нравится python и у вас есть возможность установить новый пакет python, вы можете попробовать эту утилиту python .

# install pythonp
$ pip install pythonp

$ echo $a | pythonp "l.lower()"
2
bombs

Конвертирование происходит только для алфавитов. Итак, это должно работать аккуратно.

Я сосредотачиваюсь на преобразовании алфавитов между a-z из верхнего регистра в нижний регистр. Любые другие символы должны быть просто напечатаны в стандартный вывод ...

Преобразует весь текст в пути/к/файлу/имени файла в диапазоне от -z до A-Z

Для преобразования нижнего регистра в верхний

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

Для преобразования из верхнего регистра в нижний регистр

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

Например,

имя файла:

my name is xyz

превращается в:

MY NAME IS XYZ

Пример 2:

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

Пример 3:

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

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

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

Это гораздо более быстрый вариант подхода JaredTS486 который использует собственные возможности Bash (включая версии Bash <4.0) для оптимизации его подхода.

Я рассчитал 1000 итераций этого подхода для маленькой строки (25 символов) и большей строки (445 символов) как для преобразования в нижний, так и в верхний регистр. Поскольку тестовые строки преимущественно строчные, преобразования в нижний регистр обычно выполняются быстрее, чем в верхний.

Я сравнил свой подход с несколькими другими ответами на этой странице, которые совместимы с Bash 3.2. Мой подход гораздо более производительный, чем большинство описанных здесь подходов, и в некоторых случаях он даже быстрее, чем tr.

Вот временные результаты для 1000 итераций по 25 символов:

  • 0,46 с для моего подхода к нижнему регистру; 0,96 с в верхнем регистре
  • 1.16s для подход Орвеллофила в нижний регистр; 1,59 с заглавными буквами
  • 3,67 с tr в нижний регистр; 3,81 с заглавными буквами
  • 11.12s для подход ghostdog74 в нижний регистр; 31,41с заглавными буквами
  • 26,25 с для подхода технозавра в нижнем регистре; 26.21 с заглавными буквами
  • 25.06s для подход JaredTS486 в нижний регистр; 27.04 с заглавными буквами

Результаты синхронизации для 1000 итераций по 445 символов (состоящих из поэмы "Робин" Уиттера Биннера):

  • 2 для моего подхода к строчным буквам; 12с прописными буквами
  • 4s для tr в нижний регистр; 4с заглавными буквами
  • 20 для подход Орвеллофила в нижний регистр; 29 с заглавными буквами
  • 75 для ghostdog74's подход к нижнему регистру; 669 для прописных. Интересно отметить, насколько значительна разница в производительности между тестом с преобладающими совпадениями и тестом с преобладающими промахами
  • 467s для подхода технозавра в нижний регистр; 449 для прописных
  • 660s для подход JaredTS486 в нижний регистр; 660 для прописных. Интересно отметить, что этот подход генерировал непрерывные сбои страниц (перестановка памяти) в Bash

Решение:

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

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

Некоторые характеристики производительности моего решения:

  1. Используются только встроенные утилиты Shell, что позволяет избежать накладных расходов на вызов внешних двоичных утилит в новом процессе.
  2. Избегает вложенных оболочек, которые влекут за собой снижение производительности
  3. Использует механизмы оболочки, которые скомпилированы и оптимизированы для производительности, такие как глобальная замена строк внутри переменных, обрезание суффиксов переменных, поиск и сопоставление регулярных выражений. Эти механизмы намного быстрее, чем перебирать строки вручную
  4. Зацикливает только то количество раз, которое требуется для преобразования количества уникальных совпадающих символов. Например, преобразование строки, содержащей три разных заглавных символа в строчные, требует только 3 итераций цикла. Для предварительно сконфигурированного алфавита ASCII максимальное количество итераций цикла составляет 26
  5. UCS и LCS могут быть дополнены дополнительными символами
0
Dejay Clayton