はじめに
シェルスクリプトで大文字と小文字を変換する場合 tr
コマンドを使用するのが一般的です。しかし tr
コマンドは外部コマンドであるため呼び出しが遅く、ループの中で何度も呼び出すには適していません。bash であれば変数展開を用いて大文字小文字の変換ができるのですが、dash や ksh や zsh では使用できません(ksh や zsh では declare
を使用した変換方法があります)。そこで POSIX 準拠シェルの機能だけを使って、どの POSIX シェルでも動く大文字小文字変換関数を実装してみました。
ちなみに、この方法は変数に代入された小さな文字列の変換に適していますが、大きなテキストファイルの変換は遅くなるため適していません。大きなテキストを変換する場合は以下の記事を参照してください。
実装
#!/bin/sh
set -eu
str="ABCDEFGHIJKLM:NOPQRSTUVWXYZ"
gentr() {
eval "$(
echo "$1() {"
echo ' set -- "$1" "$2" ""'
echo ' while [ "$2" ]; do'
echo ' case $2 in'
shift
while [ $# -gt 0 ]; do
echo " \"${1}\"*)" \
"set -- \"\$1\" \"\${2#\"${1}\"}\" \"\${3}${2}\" ;;"
shift 2
done
echo ' *) set -- "$1" "${2#?}" "$3${2%"${2#?}"}" ;;'
echo ' esac'
echo ' done'
echo ' eval "$1=\$3"'
echo '}'
)"
}
gentr lowercase \
A a B b C c D d E e F f G g H h I i J j K k L l M m \
N n O o P p Q q R r S s T t U u V v W w X x Y y Z z
gentr uppercase \
a A b B c C d D e E f F g G h H i I j J k K l L m M \
n N o O p P q Q r R s S t T u U v V w W x X y Y z Z
lowercase str "$str"
echo "$str" # => abcdefghijklm:nopqrstuvwxyz
uppercase str "$str"
echo "$str" # => ABCDEFGHIJKLM:NOPQRSTUVWXYZ
# 以下とほぼ同等
#
# str="ABC"
# echo "$str" | tr A-Z a-z
# echo "${str,,}"
#
# str="abc"
# echo "$str" | tr a-z A-Z
# echo "${str^^}"
解説
gentr
関数は、変換関数を生成する関数です。 gentr lowercase A a B b ・・・
のように、生成する関数名(lowercase
) の後に、変換前の文字列 (A
) と変換後の文字列 (a
) を交互に書いていくことで、以下のようなコードを生成し eval
することで関数を定義します。
lowercase() {
set -- "$1" "$2" ""
while [ "$2" ]; do
case $2 in
"A"*) set -- "$1" "${2#"A"}" "${3}a" ;;
"B"*) set -- "$1" "${2#"B"}" "${3}b" ;;
︙
"Z"*) set -- "$1" "${2#"Z"}" "${3}z" ;;
*) set -- "$1" "${2#?}" "$3${2%"${2#?}"}" ;;
esac
done
eval "$1=\$3"
}
あとはその関数を使用して変換するだけです。変換関数では外部コマンドもサブシェルも使用しないため(文字列が十分小さければ) tr
コマンドを呼び出すよりも速く変換処理を行うことが出来ます。
lowercase str "ABC"
echo "$str" # => abc
gentr
関数は汎用的に作られているので、その他の変換にも使えると思います。ただしメタ文字を変換する場合、gentr
関数に渡す文字にエスケープが必要になるかもしれないので注意してください。
例 先頭一文字を大文字にする
capitalize() {
set -- "$1" "${2%"${2#?}"}" "${2#?}"
uppercase "$1" "$2"
eval "$1=\"\${$1}\$3\""
}
capitalize str "abc"
echo "$str" # => Abc
# 以下とほぼ同等
#
# str="abc"
# echo "${str^}"
例 大文字と小文字を反転する
gentr togglecase \
A a B b C c D d E e F f G g H h I i J j K k L l M m \
N n O o P p Q q R r S s T t U u V v W w X x Y y Z z \
a A b B c C d D e E f F g G h H i I j J k K l L m M \
n N o O p P q Q r R s S t T u U v V w W x X y Y z Z
togglecase str "Abc"
echo "$str" # => aBC
# 以下とほぼ同等
#
# echo Abc | tr a-zA-Z A-Za-z
#
# str="Abc"
# echo "${str~~}"