В настоящее время я выполняю несколько модульных тестов, которые выполняются из bash. Модульные тесты инициализируются, выполняются и очищаются в сценарии bash. Этот сценарий обычно содержит функции init (), execute () и cleanup (). Но они не являются обязательными. Я хотел бы проверить, определены они или нет.
Я делал это ранее, используя greping и отправляя источник, но это казалось неправильным. Есть ли более элегантный способ сделать это?
Обновлено: следующий сниплет работает как шарм:
fn_exists()
{
LC_ALL=C type | grep -q 'shell function'
}
Вы можете сохранить grep, используя type -t
и ==
.
Не работает, если языковой стандарт не английский. type test_function
говорит test_function on funktio.
при использовании финского языка и ist eine Funktion
при использовании немецкого языка.
Для неанглийских языков на помощь приходит LC_ALL=C
Я думаю, вы ищете команду типа. Он скажет вам, является ли что-то функцией, встроенной функцией, внешней командой или просто не определено. Пример:
$ LC_ALL=C type foo
bash: type: foo: not found
$ LC_ALL=C type ls
ls is aliased to `ls --color=auto'
$ which type
$ LC_ALL=C type type
type is a shell builtin
$ LC_ALL=C type -t rvm
function
$ if [ -n "$(LC_ALL=C type -t rvm)" ] && [ "$(LC_ALL=C type -t rvm)" = function ]; then echo rvm is a function; else echo rvm is NOT a function; fi
rvm is a function
type -t $function
- это талон на питание.
Почему вы не разместили это как ответ? :-)
Потому что я опубликовал свой ответ, используя сначала объявление :-)
type [-t]
хорош для рассказать, что такое вещь, но когда тестирование, если что-то является функцией, это медленно, поскольку вам нужно перенаправить на grep или использовать обратные кавычки, оба из которых порождают подпроцесс.
Если я не прочитал неправильно, использование тип должно будет выполнить заведомо минимальный доступ, чтобы проверить, есть ли соответствующий файл. @Lloeki, вы совершенно правы, но это вариант, который дает минимальный результат, и вы все равно можете использовать уровень ошибок. Вы можете получить результат без подпроцесса, например type -t realpath > /dev/shm/tmpfile ; read < /dev/shm/tmpfile
(плохой пример). Однако объявлять - лучший ответ, поскольку у него 0 disk io.
@AllanWind Вам определенно разрешено писать несколько ответов, хотя я полагаю, что еще в 2008 году это могло быть не так.
@Lloeki проверка возвращаемого значения по-прежнему работает нормально, поэтому я мог бы использовать type -t foo && echo "foo is something"
, я не вижу причин использовать вместо него declare
, поскольку он все еще производит вывод, который, если нам нужно не показывать, мы должны перенаправить на / dev / null ...
@StevenLu что делать, если вам нужно знать, что foo - это конкретная функция? Разве это не возможно только с помощью declare (без использования подпроцесса или приведенного выше примера Орвеллофила)?
Я не знаю, кем я являюсь, но я знаю, чем я не являюсь, и это эксперт по оболочке ... при этом, похоже, declare
- ваш лучший друг, если вы можете быть удовлетворены просто значением да / нет в качестве относительно того, является ли ваш символ функцией или нет, но я в основном сравнивал как синтаксис, так и вывод type
и declare
, и было действительно очевидно, что type -t
намного более самодокументируется для сценариев оболочки общего назначения. Вы пишете сценарии оболочки. Subshell, чтобы облегчить понимание сценария? Да, черт возьми.
Другая сторона того, что я говорил, заключалась в том, что вы все еще можете обойтись без подоболочки / трубы / и т.д., если вы удовлетворены тем, что просто знаете, определен ли ваш символ или нет (и будучи функцией, это условие удовлетворяет).
Также см. gnu.org/software/bash/manual/html_node/….
Встроенная команда bash declare
имеет параметр -F
, который отображает все определенные имена функций. Если заданы аргументы имени, он покажет, какие из этих функций существуют, и, если все они это сделают, установит статус соответственно:
$ fn_exists() { declare -F "" > /dev/null; }
$ unset f
$ fn_exists f && echo yes || echo no
no
$ f() { return; }
$ fn_exist f && echo yes || echo no
yes
отлично поработал для меня. Тем более, что в моей оболочке нет флага -t для типа (у меня было много проблем с типом "$ command")
Действительно, он также работает в zsh (полезно для сценариев rc) и не требует grep для типа.
@DennisHodapp нет необходимости в type -t
, вместо этого вы можете полагаться на статус выхода. Давно пользовался type program_name > /dev/null 2>&1 && program_name arguments || echo "error"
, чтобы посмотреть, смогу ли я что-нибудь вызвать. Очевидно, type -t
и вышеупомянутый метод также позволяют определять тип, а не только то, является ли он «вызываемым».
@ 0xC0000022L что, если имя_программы не является функцией?
@DavidWiniecki: не уверен, о чем вы говорите?!? Я отвечал на другой комментарий. Если это не функция, ее все равно можно будет вызвать, и синтаксис будет таким же, будь то псевдоним, функция, встроенная или внешняя команда.
@ 0xC0000022L Я придирался к тому, как использование статуса выхода не позволяет узнать, является ли имя_программы функцией, но теперь я думаю, что вы обратились к этому, когда сказали: «Очевидно, что тип -t и вышеуказанный метод также позволяют определять тип. , а не только то, является ли он «вызываемым». Извини.
Чтобы получить 1, если функция существует, и 0, если не существует: declare -f > /dev/null && echo 1 || echo 0;
Какого черта? Любое описание? Обычно такие ответы на StackOverflow
просто смиряются с -1
.
Я добавил описание по запросу. Добейтесь голосов :-)
Я бы улучшил его до:
fn_exists()
{
type 2>/dev/null | grep -q 'is a function'
}
И используйте это так:
fn_exists test_function
if [ $? -eq 0 ]; then
echo 'Function exists!'
else
echo 'Function does not exist...'
fi
Это говорит вам, существует ли он, но не то, что это функция
fn_exists()
{
type >/dev/null 2>&1;
}
Копаем старый пост ... но недавно я использовал это и протестировал обе альтернативы, описанные с помощью:
test_declare () {
a () { echo 'a' ;}
declare -f a > /dev/null
}
test_type () {
a () { echo 'a' ;}
type a | grep -q 'is a function'
}
echo 'declare'
time for i in $(seq 1 1000); do test_declare; done
echo 'type'
time for i in $(seq 1 100); do test_type; done
это сгенерировало:
real 0m0.064s
user 0m0.040s
sys 0m0.020s
type
real 0m2.769s
user 0m1.620s
sys 0m1.130s
объявить шибко быстрее!
Это можно сделать без grep: test_type_nogrep () { a () { echo 'a' ;}; local b=$(type a); c=${b//is a function/}; [ $? = 0 ] && return 1 || return 0; }
@qneill Я провел несколько более обширный тест в мой ответ,
ТРУБА - самый медленный элемент. Этот тест не сравнивает type
и declare
. Он сравнивает type | grep
с declare
. Это большая разница.
Можно использовать 'type' без каких-либо внешних команд, но вы должны вызвать его дважды, поэтому он все равно будет примерно в два раза медленнее, чем версия 'declare':
test_function () {
! type -f >/dev/null 2>&1 && type -t >/dev/null 2>&1
}
К тому же это не работает в POSIX sh, так что ничего не стоит, кроме как пустяков!
test_type_nogrep () {а () {эхо 'а';}; local b = $ (тип a); c = $ {b // это функция /}; [$? = 0] && return 1 || возврат 0; } - qneill
Пользуясь другими решениями и комментариями, я пришел к следующему:
fn_exists() {
# appended double quote is an ugly trick to make sure we do get a string -- if is not a known command, type does not output anything
[ `type -t `"" == 'function' ]
}
Используется как ...
if ! fn_exists $FN; then
echo "Hey, $FN does not exist ! Duh."
exit 2
fi
Он проверяет, является ли данный аргумент функцией, и избегает перенаправлений и другого поиска.
Приятно, мой фаворит из группы! Разве вам не нужны двойные кавычки вокруг аргумента? Как в [ $(type -t "")"" == 'function' ]
Спасибо @quickshiftin; Я не знаю, нужны ли мне эти двойные кавычки, но вы, вероятно, правы, хотя ... может ли функция даже быть объявлена с именем, которое нужно было бы заключить в кавычки?
Вы используете bash, используйте [[...]]
вместо [...]
и избавьтесь от взлома цитат. Также делает вилку обратными кавычками, что работает медленно. Вместо этого используйте declare -f > /dev/null
.
Избегая ошибок с пустыми аргументами, сокращая кавычки и используя posix-совместимое равенство '=', его можно безопасно уменьшить до :: fn_exists() { [ x$(type -t ) = xfunction ]; }
Особенно понравилось решение от Грегори Жозеф
Но я немного изменил его, чтобы обойти "уродливый трюк с двойными кавычками":
function is_executable()
{
typeset TYPE_RESULT = "`type -t `"
if [ "$TYPE_RESULT" == 'function' ]; then
return 0
else
return 1
fi
}
Если declare в 10 раз быстрее, чем test, это кажется очевидным ответом.
Обновлено: ниже параметр -f
является излишним для BASH, не стесняйтесь его опускать. Лично мне сложно вспомнить, какой вариант что делает, поэтому я просто использую оба. -f показывает функции, а -F показывает имена функций.
#!/bin/sh
function_exists() {
declare -f -F > /dev/null
return $?
}
function_exists function_name && echo Exists || echo No such function
Параметр "-F" для объявления заставляет его возвращать только имя найденной функции, а не все содержимое.
Не должно быть заметного снижения производительности при использовании / dev / null, и если это вас так сильно беспокоит:
fname=`declare -f -F `
[ -n "$fname" ] && echo Declare -f says $fname exists || echo Declare -f says does not exist
Или скомбинируйте то и другое для собственного бессмысленного удовольствия. Они оба работают.
fname=`declare -f -F `
errorlevel=$?
(( ! errorlevel )) && echo Errorlevel says exists || echo Errorlevel says does not exist
[ -n "$fname" ] && echo Declare -f says $fname exists || echo Declare -f says does not exist
Параметр "-f" является избыточным.
Параметр -F
не существует в zsh (полезно для переносимости)
-F
также не нужен: кажется, что он подавляет только определение / тело функции.
@blueyed Это может быть необязательно, но очень желательно, мы пытаемся подтвердить, что функция существует, а не перечислить все ее содержимое (что несколько неэффективно). Вы бы проверили наличие файла с помощью cat "$fn" | wc -c
? Что касается zsh, если тег трепать вас не подсказал, возможно, сам вопрос должен быть. «Определить, существует ли функция в bash». Я также хотел бы указать, что, хотя параметр -F
не существует в zsh, он также не вызывает ошибки, поэтому использование и -f, и -F позволяет успешной проверке как в zsh, так и в трепать, что в противном случае не было бы ' т.
@Orwellophile -F
используется в zsh для чисел с плавающей запятой. Я не понимаю, почему использование -F
делает его лучше в bash ?! У меня сложилось впечатление, что declare -f
работает так же в bash (относительно кода возврата).
@blueyed: в трепать-F
пропускает определение функции, а -f
лишний, но безвредный. Правильный ответ был на самом деле declare -f "$fn"
. По неизвестным причинам я использовал оба флага. Позже было чистой случайностью обнаружить, что в zsh флаг -F
не имеет смысла, но безвреден. Учитывая, что declare -fF "$fn"
работает в обеих оболочках и не требует от меня двойной проверки аргументов, это мгновенный выигрыш для меня. И если вы не понимаете, почему -F предпочтительнее -f, то вы, вероятно, считаете cat "$filename" || fail
эквивалентным. согласно test -f "$filename" || fail
. Тот же принцип.
@Orwellophile У меня сложилось впечатление, что -F
может быть вредным для zsh, но, видимо, это не так.
return $?
является избыточным, удалите его, и все равно будет возвращен последний статус выхода.
Это сводится к использованию 'declare' для проверки вывода или кода выхода.
Стиль вывода:
isFunction() { [[ "$(declare -Ff "")" ]]; }
Использование:
isFunction some_name && echo yes || echo no
Однако, если память не используется, перенаправление на null выполняется быстрее, чем подстановка вывода (кстати, ужасный и устаревший метод cmd должен быть изгнан и вместо него должен использоваться $ (cmd)). И поскольку declare возвращает true / false, если найдено / не найден, и функции возвращают код выхода последней команды в функции, поэтому явный возврат обычно не требуется, а поскольку проверка кода ошибки выполняется быстрее, чем проверка строкового значения (даже пустой строки):
Стиль статуса выхода:
isFunction() { declare -Ff "" >/dev/null; }
Это, вероятно, настолько лаконично и мягко, насколько это возможно.
Для максимальной лаконичности используйте isFunction() { declare -F ""; } >&-
.
isFunction() { declare -F -- "$@" >/dev/null; }
- моя рекомендация. Он также работает со списком имен (успешно, только если все являются функциями), не вызывает проблем с именами, начинающимися с -
, и, на моей стороне (bash
4.2.25), declare
всегда терпит неудачу, когда вывод закрывается с помощью >&-
, потому что он не может записать имя в стандартный вывод в этом случае
И имейте в виду, что на некоторых платформах echo
может иногда давать сбой из-за «прерванного системного вызова». В этом случае "check && echo yes || echo no" все еще может выводить no
, если check
истинно.
fn_exists()
{
[[ $(type -t ) == function ]] && return 0
}
Обновить
isFunc ()
{
[[ $(type -t ) == function ]]
}
$ isFunc isFunc
$ echo $?
0
$ isFunc dfgjhgljhk
$ echo $?
1
$ isFunc psgrep && echo yay
yay
$
Тестируем разные решения:
#!/bin/bash
test_declare () {
declare -f f > /dev/null
}
test_declare2 () {
declare -F f > /dev/null
}
test_type () {
type -t f | grep -q 'function'
}
test_type2 () {
[[ $(type -t f) = function ]]
}
funcs=(test_declare test_declare2 test_type test_type2)
test () {
for i in $(seq 1 1000); do ; done
}
f () {
echo 'This is a test function.'
echo 'This has more than one command.'
return 0
}
post='(f is function)'
for j in 1 2 3; do
for func in ${funcs[@]}; do
echo $func $post
time test $func
echo exit code $?; echo
done
case $j in
1) unset -f f
post='(f unset)'
;;
2) f='string'
post='(f is string)'
;;
esac
done
выходы, например:
test_declare (f is function)
real 0m0,055s user 0m0,041s sys 0m0,004s exit code 0
test_declare2 (f is function)
real 0m0,042s user 0m0,022s sys 0m0,017s exit code 0
test_type (f is function)
real 0m2,200s user 0m1,619s sys 0m1,008s exit code 0
test_type2 (f is function)
real 0m0,746s user 0m0,534s sys 0m0,237s exit code 0
test_declare (f unset)
real 0m0,040s user 0m0,029s sys 0m0,010s exit code 1
test_declare2 (f unset)
real 0m0,038s user 0m0,038s sys 0m0,000s exit code 1
test_type (f unset)
real 0m2,438s user 0m1,678s sys 0m1,045s exit code 1
test_type2 (f unset)
real 0m0,805s user 0m0,541s sys 0m0,274s exit code 1
test_declare (f is string)
real 0m0,043s user 0m0,034s sys 0m0,007s exit code 1
test_declare2 (f is string)
real 0m0,039s user 0m0,035s sys 0m0,003s exit code 1
test_type (f is string)
real 0m2,394s user 0m1,679s sys 0m1,035s exit code 1
test_type2 (f is string)
real 0m0,851s user 0m0,554s sys 0m0,294s exit code 1
Так что declare -F f
кажется лучшим решением.
Внимание: declare -F f
не возвращает ненулевое значение, если f не существует на zsh, но bash да. Будьте осторожны при его использовании. declare -f f
, с другой стороны, работает так, как ожидалось, добавляя определение функции к stdout (что может раздражать ...)
Вы пробовали test_type3 () { [[ $(type -t f) = function ]] ; }
, есть предельная стоимость определения локальной переменной (хотя и <10%)
Из моего комментария к другому ответу (который мне не хватает, когда я возвращаюсь на эту страницу)
$ fn_exists() { test x$(type -t ) = xfunction; }
$ fn_exists func1 && echo yes || echo no
no
$ func1() { echo hi from func1; }
$ func1
hi from func1
$ fn_exists func1 && echo yes || echo no
yes
Известное имя функции. Скажем, имя my_function
, затем используйте
[[ "$(type -t my_function)" == 'function' ]] && my_function;
# or
[[ "$(declare -fF my_function)" ]] && my_function;
Имя функции в переменной. Если мы объявим func=my_function
, мы сможем использовать
[[ "$(type -t $func)" == 'function' ]] && $func;
# or
[[ "$(declare -fF $func)" ]] && $func;
Логическая инверсия с тем же результатом. Все сразу. Здесь мы используем ||
вместо &&
.
[[ "$(type -t my_function)" != 'function' ]] || my_function;
[[ ! "$(declare -fF my_function)" ]] || my_function;
func=my_function
[[ "$(type -t $func)" != 'function' ]] || $func;
[[ ! "$(declare -fF $func)" ]] || $func;
Опасная ситуация при использовании ||
с return
внутри функции
Следующая комбинация заставит вас завершить процесс оболочки.
# Some script execution strict mode. The essence is in the "-e"
set -euf +x -o pipefail
function run_if_exists(){
my_function=
[[ "$(type -t my_function)" != 'function' ]] || return;
$my_function
}
run_if_exists non_existing_function
Это потому, что приведенное выше эквивалентно
set -e
function run_if_exists(){
return 1;
}
run_if_exists
который всегда будет терпеть неудачу.
Чтобы предотвратить это, вы должны использовать || { true; return; }
или что-то еще в таком предусловии вашей функции.
[[ "$(type -t my_function)" != 'function' ]] || { true; return; }
Спасибо. Я использовал это, чтобы условно определить заглушенные версии функций при загрузке библиотеки оболочки.
fn_exists foo || foo() { :; }