Импорт функций из оболочки script
У меня есть оболочка script, которую я хотел бы протестировать с помощью shUnit. script (и все функции) находятся в одном файле, так как упрощают установку.
Пример для script.sh
#!/bin/sh
foo () { ... }
bar () { ... }
code
Я хотел написать второй файл (который не нужно распространять и устанавливать) для проверки функций, определенных в script.sh
Что-то вроде run_tests.sh
#!/bin/sh
. script.sh
# Unit tests
Теперь проблема заключается в .
(или source
в Bash). Он не только анализирует определения функций, но также выполняет код в script.
Так как script без аргументов ничего плохого я не мог
. script.sh > /dev/null 2>&1
но я блуждал, если есть лучший способ достичь моей цели.
Edit
Мое предложение об обходном пути не работает в случае вызова script exit
, поэтому мне нужно уловить выход
#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
...
}
. script.sh
Вызывается функция run_tests
, но как только я перенаправляю вывод исходной команды, функции в script не анализируются и недоступны в обработчике trap
Это работает, но я получаю вывод script.sh
:
#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
function_defined_in_script_sh
}
. script.sh
Это не печатает вывод, но я получаю сообщение об ошибке, что функция не определена:
#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
function_defined_in_script_sh
}
. script.sh | grep OUTPUT_THAT_DOES_NOT_EXISTS
Это не выводит вывод, а обработчик ловушки run_tests
не вызывается вообще:
#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
function_defined_in_script_sh
}
. script.sh > /dev/null
Ответы
Ответ 1
В соответствии с разделом "Команды встроенной оболочки" bash manpage, .
aka source
принимает необязательный список аргументы, которые передаются в script. Вы можете использовать это, чтобы ввести опцию do-nothing. Например, script.sh
может быть:
#!/bin/sh
foo() {
echo foo $1
}
main() {
foo 1
foo 2
}
if [ "${1}" != "--source-only" ]; then
main "${@}"
fi
и unit.sh
могут быть:
#!/bin/bash
. ./script.sh --source-only
foo 3
Тогда script.sh
будет вести себя нормально, а unit.sh
будет иметь доступ ко всем функциям из script.sh
, но не будет вызывать код main()
.
Обратите внимание, что дополнительные аргументы для source
не находятся в POSIX, поэтому /bin/sh
может не обрабатывать его - следовательно, #!/bin/bash
в начале unit.sh
.
Ответ 2
Поднял эту технику из Python, но концепция прекрасно работает в bash или любой другой оболочке...
Идея состоит в том, что мы превращаем раздел главного кода нашего script в функцию. Затем в самом конце script мы помещаем оператор if, который будет вызывать эту функцию только в том случае, если мы выполнили script, но не если мы ее получили. Затем мы явно вызываем функцию script() из наших "runtests" script, которая получила 'script' script и, следовательно, содержит все ее функции.
Это зависит от того, что, если мы выберем script, переменная среды bash -maintained $0
, которая является именем выполняемого script, будет именем вызов (родительский) script (runtests
в этом случае), а не источник script.
(Я переименовал script.sh
только script
, потому что .sh
является избыточным и меня смущает.: -)
Ниже приведены два сценария. Некоторые примечания...
-
[email protected]
оценивает все аргументы, переданные функции или
script как отдельные строки. Если вместо этого мы использовали $*
, все
аргументы будут объединены в одну строку.
-
RUNNING="$(basename $0)"
требуется, так как $0
всегда включает в себя
по крайней мере, текущий префикс каталога, как в ./script
.
- Тест
if [[ "$RUNNING" == "script" ]]...
. это волшебство, которое вызывает
script
, чтобы вызвать функцию script(), только если script
был запущен напрямую
из командной строки.
script
#!/bin/bash
foo () { echo "foo()"; }
bar () { echo "bar()"; }
script () {
ARG1=$1
ARG2=$2
#
echo "Running '$RUNNING'..."
echo "script() - all args: [email protected]"
echo "script() - ARG1: $ARG1"
echo "script() - ARG2: $ARG2"
#
foo
bar
}
RUNNING="$(basename $0)"
if [[ "$RUNNING" == "script" ]]
then
script "[email protected]"
fi
runtests
#!/bin/bash
source script
# execute 'script' function in sourced file 'script'
script arg1 arg2 arg3
Ответ 3
Если вы используете Bash, аналогичное решение для подхода @andrewdotn (но без необходимости использования дополнительного флага или в зависимости от имени script) может быть выполнено с помощью массива BASH_SOURCE
.
script.sh:
#!/bin/bash
foo () { ... }
bar () { ... }
main() {
code
}
if [[ "${#BASH_SOURCE[@]}" -eq 1 ]]; then
main "[email protected]"
fi
run_tests.sh:
#!/bin/bash
. script.sh
# Unit tests