LoginSignup
0
0

More than 1 year has passed since last update.

シェルスクリプトだけで大文字⇔小文字変換(POSIX準拠シェル用)

Posted at

はじめに

シェルスクリプトで大文字と小文字を変換する場合 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~~}"
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0