Чтобы суммировать все другие решения и частично исправить их, вот решение, которое:
- не использует
declare
дважды
- не нуждается во внешних программах (например,
tail
)
- не делает неожиданных замен
- относительно короткий
- защищает вас от обычных программных ошибок благодаря правильному цитированию
Но:
- Вероятно, он не работает с рекурсивными функциями, поскольку имя функции, используемое для рекурсии внутри копии, не заменяется. Правильно подобрать замену - слишком сложная задача. Если вы хотите использовать такие замены, вы можете попробовать этот ответ https://stackoverflow.com/a/18839557 с
eval "${_//$1/$2}"
вместо eval "${_/$1/$2}"
(обратите внимание на двойной //
). Однако замена имени завершается неудачно для слишком простых имен функций (например, a
) и не может быть выполнена для вычисляемой рекурсии (например, command_help() { case "$1" in ''|-help) echo "help [command]"; return;; esac; "command_$1" -help "${@:2}"; }
)
Все вместе:
: rename_fn oldname newname
rename_fn()
{
local a
a="$(declare -f "$1")" &&
eval "function $2 ${a#*"()"}" &&
unset -f "$1";
}
сейчас тесты:
somefn() { echo one; }
rename_fn somefn thatfn
somefn() { echo two; }
somefn
thatfn
выводит по мере необходимости:
two
one
Теперь попробуйте несколько более сложных случаев, которые все дают ожидаемые результаты или неудачи:
rename_fn unknown "a b"; echo $?
rename_fn "a b" murx; echo $?
a(){ echo HW; }; rename_fn " a " b; echo $?; a
a(){ echo "'HW'"; }; rename_fn a b; echo $?; b
a(){ echo '"HW"'; }; rename_fn a b; echo $?; b
a(){ echo '"HW"'; }; rename_fn a "b c"; echo $?; a
Можно утверждать, что следующее по-прежнему ошибка:
a(){ echo HW; }; rename_fn a " b "; echo $?; b
, поскольку это должно произойти сбой, поскольку " b "
не является правильным именем функции Если вы действительно этого хотите, вам нужен следующий вариант:
rename_fn()
{
local a
a="$(declare -f "$1")" &&
eval "function $(printf %q "$2") ${a#*"()"}" &&
unset -f "$1";
}
Теперь это ловит и этот искусственный случай. (Обратите внимание, что printf
с %q
является bash
встроенным.)
Конечно, вы можете разделить это на копии + переименовать так:
copy_fn() { local a; a="$(declare -f "$1")" && eval "function $(printf %q "$2") ${a#*"()"}"; }
rename_fn() { copy_fn "$@" && unset -f "$1"; }
Надеюсь, это 101% решение. Если это нуждается в улучшении, пожалуйста, прокомментируйте;)