ФЭНДОМ


Сценарий на баше, который умеет печатать в заданном падеже и роде порядковые числительные — в этой реализации от 0 до 999. В конце вывода нет символа перевода строки или пробела — мало ли где пригодится этот сценарий.

#!/bin/sh

NUMBER=1
GENUS=m #masculine
CASE=i #именительный падеж
MAX_SUPPORTED_NUMBER=999

print_help()
{
	echo Вывод порядкового числительного на русском языке в мужском роде единственного числа для заданного числа
	echo Поддерживаются числа от 0 до $MAX_SUPPORTED_NUMBER
	echo -e "Пример: \e[1mordinal -fcp 24\e[0m выведет «$($0 -fcp 24)»"
	echo -e "Число должно быть обязательно после параметров\n"
	echo Параметры сценария:
	echo -e "\t\e[1m-f\e[0m — в женском роде (feminine)"
	echo -e "\t\e[1m-m\e[0m — в мужском роде (masculine)"
	echo -e "\t\e[1m-n\e[0m — в среднем роде (neuter)\n"
	echo -e "\t\e[1m-p\e[0m — во множественном числе (plural)"
	echo -e "\t\e[1m-s\e[0m — в единственном числе (singular)\n"
	echo -e "\t\e[1m-u\e[0m — с большой буквы (upper)\n"
	echo -e "\t\e[1m-c\e[0m — падеж (case). Этот флаг требует параметра\n\t\tДопустимые значения параметра:\e[1mi\e[0m|\e[1mr\e[0m|\e[1md\e[0m|\e[1mt\e[0m|\e[1mp\e[0m\n\t\tПо умолчанию используется именительный.\n\t\tВинительный падеж изъят как неоднозначный, используйте именительный или родительный"
	echo -e "\t\e[1m-h\e[0m — Справка"
	echo -e "\t\e[1m-v\e[0m — Информация об авторе"
}

print_version() { echo Автор gluk47@gmail.com. Лицензия GPL v3. Версия 0.7; }

process_opts ()
{
	while getopts "hvpsmfnuc:" cur_opt $1; do
		case "$cur_opt" in
			h) print_help; EXIT_BY_CMD=yes;;
			v) print_version; EXIT_BY_CMD=yes;;
			m) GENUS=m;;
			f) GENUS=f;;
			n) GENUS=n;;
			s) [ -z "$GENUS" ] && GENUS=m;; #default
			p) GENUS=p;; #plural
			u) CAPITALIZE=yes;;
			c) CASE=$OPTARG;;
		esac
	done
}

# Сгенерировать корень количественного числительного
# Параметр [inout] __цифра__ (число из [0;9])
gen_cardinal_base ()
{
	local p=$(eval 'echo -n "$'$1'"')
	case $p in
		0) eval $1=нол;;
		1) eval $1=один;;
		2)	if [ "$2" == -f ]; then eval $1=две
			else eval $1=два
			fi;;
		3) eval $1=три;;
		4) eval $1=четыр;;
		5) eval $1=пять;;
		6) eval $1=шесть;;
		7) eval $1=семь;;
		8) eval $1=восемь;;
		8ь) eval $1=восьмь;;
		9) eval $1=девять;;
		*) eval $1=???;
	esac
}

# Cгенерировать корень слова. Параметр [inout] — число, для которого надо сгенерировать
# Глобальная переменная NUMBER может измениться при вызове gen_tens. Для правильного окончания *40 → *42
gen_base()
{
	local p=$(eval 'echo -n "$'$1'"')
	if [[ $p -le 19 ]]; then
		case $p in
			0) eval $1=нулев;;
			1) eval $1=перв;;
			2) eval $1=втор;;
			3) eval $1=трет;;
			4) eval $1=четвёрт;;
			5) eval $1=пят;;
			6) eval $1=шест;;
			7) eval $1=седьм;;
			8) eval $1=восьм;;
			9) eval $1=девят;;
			10) eval $1=десят;;
			11) eval $1=одиннадцат;;
			12) eval $1=двенадцат;;
			13) eval $1=тринадцат;;
			14) eval $1=четырнадцат;;
			15) eval $1=пятнадцат;;
			16) eval $1=шестнадцат;;
			17) eval $1=семнадцат;;
			18) eval $1=восемнадцат;;
			19) eval $1=девятнадцат;;
		esac
	else if [[ $p -le 99 ]]; then gen_tens $1
	else gen_hundreds $1
	fi fi
}

# Обработать двузначное число
# Параметр [inout] число целиком
# Глобальная переменная NUMBER может измениться. Для правильного окончания *40 → *42
gen_tens ()
{
	local p=$(eval 'echo -n "$'$1'"')
	local d
	let "d=$p/10%10"
	local dr
	let "dr=$p%10"
	[[ $d -eq 8 && $dr -eq 0 ]] && d=8ь
	case $d in
		2|3) gen_cardinal_base d; d=${d}дцат; [ "$dr" -ne 0 ] && d=${d}ь;;
		4) 	d=сорок
			if [ "$dr" -eq 0 ]; then
				d=${d}ов
				NUMBER=$(($NUMBER+2)) #для корректного определения окончания
			fi;;
		5|6|7|8ь|8) gen_cardinal_base d;
			[ "$dr" -eq 0 ] && d=${d%ь}и;
			d=${d}десят;;
		9) d=девяност;
			[ "$dr" -ne 0 ] && d=${d}о;;
	esac

	if [ "$dr" -ne 0 ]; then
		gen_base dr
		eval "$1=\"$d $dr\""
	else
		eval "$1=\"$d\""
	fi
}

gen_hundreds()
{
	local p=$(eval 'echo -n "$'$1'"')
	local h
	let "h=p/100%10"
	local r
	let "hr=$p%100"
	local ans

	if [[ $hr -ne 0 ]]; then
		case $h in
			1) ans=сто;;
			2) ans=двести;;
			3) ans=триста;;
			4) ans=четыреста;;
			*) ans=$h;
				gen_cardinal_base ans;
				ans=${ans}сот;;
		esac
		gen_base hr
		eval "$1=\"$ans $hr\""
	else
		[[ $h -eq 8 ]] && h=8ь
		case $h in
			1) ans="";;
			2) ans=двух;;
			3) ans=трёх;;
			4) ans=четырёх;;
			*) ans=$h
				gen_cardinal_base ans;
				ans=${ans%ь}и;;
		esac
		ans=${ans}сот
		eval "$1=\"$ans\""
	fi
}

gen_ending()
{
	local r
	let "r=$1 % 10"
	local d
	let "d=$1/10%10"

	case $GENUS in
		f)
			if [ $r -eq 3 -a $1 -ne 13 ]; then
				if [ $CASE == i ]; then ENDING=ья
				else ENDING=ьей
				fi
			else if [ $CASE == i ]; then ENDING=ая
				else ENDING=ой
				fi
			fi
			return;;
		p)	case $CASE in
				i) ENDING=ые;;
				r|p) ENDING=ых;;
				d) ENDING=ым;;
				t) ENDING=ыми;;
			esac
			return;;
	# именительный падеж мужского и среднего рода строятся по-разному
		n)
			if [ $CASE == i ]; then
				if [ $r -eq 3 -a $1 -ne 13 ]; then ENDING=ье
				else ENDING=ое
				fi
				return;
			fi;;
		m)
			if [ $CASE == i ]; then
				if [[ $d -eq 1 ]]; then ENDING=ый
				else if [[ $r -eq 3 ]]; then ENDING=ий
				else if [[ $r == 2 || $r -ge 6 && $r -le 8 ]]; then ENDING=ой
				else ENDING=ый #именно так минимизируется число сравнений
				fi fi fi
				return
			fi;;
		*) ENDING=??;;
	esac
	# косвенный падеж мужского или среднего рода
	if [ $r -eq 3 -a $1 -ne 13 ]; then
		case $CASE in
			r)	ENDING=ьего;;
			d)	ENDING=ьему;;
			t)	ENDING=ьим;;
			p)	ENDING=ьем;;
		esac
	else
		case $CASE in
			r)	ENDING=ого;;
			d)	ENDING=ому;;
			t)	ENDING=ым;;
			p)	ENDING=ом;;
		esac
	fi
}

### main ###
if [ "$1" == unittest ]; then
	shift 1
	for ((i=0; i<=9; i++)); do "$0" $* ${i}42; echo ""; done
	exit 0
fi
process_opts "$*"
[ -n "$EXIT_BY_CMD" ] && exit 0
shift $(($OPTIND - 1))
if [[ "$1" == --help ]]; then
	print_help; exit 0;
fi

[ -n "$2" ] && echo "Вероятно, вы указали параметр после числа. В данной версии это не поддерживается" >&2
if [ -z "$1" ]; then
	echo -e "Укажите число аргументом сценария или \e[1m-h\e[0m для справки"
	exit 3;
fi
NUMBER=$1
USER_REQUESTED_NUMBER=$1
BASE=$NUMBER
gen_base BASE #NUMBER может измениться. Для правильного окончания *40 → *42
[[ $NUMBER -eq 0 ]] && ((NUMBER += 2))
gen_ending $NUMBER
WORD="$BASE$ENDING"
echo -n $WORD
[[ $USER_REQUESTED_NUMBER -gt $MAX_SUPPORTED_NUMBER ]] && exit 1
exit 0

Примеры:

$ ordinal -f -cp 242
двести сорок второй
$ ordinal -n 42
сорок второе
$ ordinal 1000
нолисотый
$ ordinal -h
Вывод порядкового числительного на русском языке в мужском роде единственного числа для заданного числа
Поддерживаются числа от 0 до 999
Пример: ordinal -fcp 24 выведет «двадцать четвёртой»
Число должно быть обязательно после параметров

Параметры сценария:
        -f — в женском роде (feminine)
        -m — в мужском роде (masculine)
        -n — в среднем роде (neuter)

        -p — во множественном числе (plural)
        -s — в единственном числе (singular)

        -u — с большой буквы (upper)

        -c — падеж (case). Этот флаг требует параметра
                Допустимые значения параметра:i|r|d|t|p
                По умолчанию используется именительный.
                Винительный падеж изъят как неоднозначный, используйте именительный или родительный
        -h — Справка
        -v — Информация об авторе