Как использовать заглавные буквы + объединять слова в строке?
(первая буква в верхнем регистре, а все остальные буквы в нижнем регистре)
пример: input = "jAMeS bOnD"
output = "JamesBond"
Как использовать заглавные буквы + объединять слова в строке?
(первая буква в верхнем регистре, а все остальные буквы в нижнем регистре)
пример: input = "jAMeS bOnD"
output = "JamesBond"
Манипуляции со строками доступны в bash версии 4:
${variable,,}
, чтобы строчные все буквы${variable^}
до заглавной первой буквы каждого слова${words[*]^}
вместо ${words[@]^}
для сохранения некоторых строк скриптаИ другие улучшения от mklement0 (см. Его комментарии):
ARRAY
-> words
)local
, чтобы не повлиять на IFS
вне функции (одного раза достаточно)local
для всех остальных локальных переменных (переменную можно сначала объявить, а затем присвоить)ARRAY=( $LOWERCASE )
may expands globs (filename wildcards)
set -f
or shopt -so noglob
read -ra words <<< "$input"
вместо words=( $input )
capitalize_remove_spaces()
{
local words IFS
read -ra words <<< "${@,,}"
IFS=''
echo "${words[*]^}"
}
Если вы хотите сохранить только буквенно-цифровые символы, расширяет встроенную переменную IFS непосредственно перед read -ra words
операция:
capitalize_remove_punctuation()
{
local words IFS=$' \t\n-\'.,;!:*?' #Handle hyphenated names and punctuation
read -ra words <<< "${@,,}"
IFS=''
echo "${words[*]^}"
}
> capitalize_remove_spaces 'jAMeS bOnD'
JamesBond
> capitalize_remove_spaces 'jAMeS bOnD *'
JamesBond*
> capitalize_remove_spaces 'Jean-luc GRAND-PIERRE'
Jean-lucGrand-pierre
> capitalize_remove_punctuation 'Jean-luc GRAND-PIERRE'
JeanLucGrandPierre
> capitalize_remove_punctuation 'Jean-luc GRAND-PIERRE *'
JeanLucGrandPierre
$LOWERCASE
go :). Создание массивов слов с ARRAY=( $LOWERCASE )
имеет ловушку: строка подлежит расширению имени пути, поэтому результаты будут неожиданными, если строка содержит глобус, такой как *
. Включение set -f
(shopt -so noglob
) (временно) может исправить это. Другой вариант - использовать read -a
для создания массива (см. Мой ответ).
- person mklement0; 11.06.2014
local IFS=''
- это дает дополнительное преимущество в виде отсутствия необходимости восстанавливать $IFS
при выходе из функции.
- person mklement0; 11.06.2014
local
при назначении $IFS
во второй раз, я бы объявил переменную только один раз, вверху. Например, во второй функции вы можете использовать local words IFS=$' \t\n-\'.,;!:*?'
(или использовать local
, чтобы просто объявить, затем назначить позже), а затем просто IFS=''
позже.
- person mklement0; 11.06.2014
Из других сообщений я придумал этот рабочий сценарий:
str="jAMeS bOnD"
res=""
split=`echo $str | sed -e 's/ /\n/g'` # Split with space as delimiter
for word in $split; do
word=${word,,} # Lowercase
word=${word^} # Uppercase first letter
res=$res$word # Concatenate result
done
echo $res
Использованная литература:
sed
и промежуточной переменной $split
: использование строки без кавычек с for
выполняет разделение слов также с пробелами, а не только с новой строкой; таким образом, вы можете просто сказать: for word in $str; do
. В любом случае есть ловушка: строки без кавычек, используемые с for
, подлежат расширению имени пути, поэтому результаты будут неожиданными, если строка содержит глобус, например *
. Включение set -f
(shopt -so noglob
) (временно) может исправить это. Обратите внимание, что для вашего решения требуется bash 4 или выше.
- person mklement0; 11.06.2014
Использование awk немного многословно, но выполняет свою работу:
s="jAMeS bOnD"
awk '{for (i=1; i<=NF; i++)
printf toupper(substr($i, 1, 1)) tolower(substr($i,2)); print ""}' <<< "$s"
JamesBond
awk: (FILENAME=- FNR=1) fatal: printf: no arguments
. Мне пришлось переместиться вниз printf
в той же строке, что и toupper...
. Ваше здоровье :)
- person oHo; 09.06.2014
Вот bash 3+
решение, которое использует tr
для преобразования регистра (операторы преобразования регистра (,
, ^
, ...) были введены в bash
4):
input="jAMeS bOnD"
read -ra words <<<"$input" # split input into an array of words
output="" # initialize output variable
for word in "${words[@]}"; do # loop over all words
# add capitalized 1st letter
output+="$(tr '[:lower:]' '[:upper:]' <<<"${word:0:1}")"
# add lowercase version of rest of word
output+="$(tr '[:upper:]' '[:lower:]' <<<"${word:1}")"
done
Примечание:
words=( $input )
для разделения входной строки на массив слов, но есть одна проблема: строка подлежит расширению имени пути, поэтому, если слово оказывается действительным глобусом (например, , *
), он будет расширен (заменен соответствующими именами файлов), что нежелательно; использование read -ra
для создания массива позволяет избежать этой проблемы (-a
считывает в массив, -r
отключает интерпретацию \
символов во входных данных).