2015-11-24 3 views
5

У меня есть сценарий Bash, который является источником. Когда этот скрипт создан, он запускает функцию в сценарии Bash. Эта функция должна прекращать выполнение скрипта, если выполняется определенное условие. Как это можно сделать, не прерывая оболочку, в которой работает сценарий?Как вырваться из исходной функции скрипта Bash

Чтобы быть ясным: я хочу, чтобы действие завершения было завершено функцией в сценарии исходной оболочки, а не в основной части исходного сценария оболочки. Проблемы, которые я вижу, это то, что return просто возвращается из функции в основную часть скрипта, а exit 1 завершает вызывающую оболочку.

Следующий минимальный пример иллюстрирует проблему:

main(){ 
    echo "starting test of environment..." 
    ensure_environment 
    echo "environment safe -- starting other procedures..." 
} 

ensure_environment(){ 
    if [ 1 == 1 ]; then 
     echo "environment problemm -- terminating..." 
     # exit 1 # <-- terminates calling shell 
     return # <-- returns only from function, not from sourced script 
    fi 
} 

main 
+1

Выполнение сценария вместо этого. Таким образом, 'exit' выйдет из подоболочки, в которой он запущен. – fedorqui

+1

Интересно. Не могли бы вы дать небольшой пример для начала, который можно было бы улучшить. Например. который завершает оболочку. –

+0

Вы просите что-то, что невозможно. Вы должны перестроить свой код. –

ответ

2

Это рецепт того, как вы можете достичь своей цели с помощью своего подхода. Я не буду писать ваш код для вас, просто расскажите, как это можно сделать.

Ваша цель - установить/изменить переменные среды в текущей оболочке bash, эффективно, используя возможно сложный сценарий оболочки. Некоторые компоненты этого скрипта могут решить, что выполнение этого сценария должно прекратиться. Сложно то, что это решение не обязательно должно быть на верхнем уровне, но может быть расположено во вложенном вызове функции. return, то не помогает, и exit прекратит использование источника поиска, что нежелательно.

Ваша задача облегчалась этим утверждением твои:

дополнительная сложность, что я не могу включать в минимальном примере делает его очень желательно, чтобы централизовать процедуру завершения в функции.

Это, как вы это делаете:

Вместо поиск вашего реального сценария, который решает, какая среду, чтобы установить, в какой («realscript.bash„), вы источник другого сценария“ipcscript.bash».

ipcscript.bash будет устанавливать некоторые межпроцессные связи. Это может быть канал на каком-то дополнительном дескрипторе файла, который вы открываете с помощью exec, это может быть временный файл, это может быть что-то другое.

ipcscript.bash затем начнет realscript.bash в качестве дочернего процесса. Это означает, что изменение окружения, которое realscript.bash делает в первую очередь, влияет только на окружение этого дочернего процесса экземпляра bash. Начиная с realscript.bash в качестве дочернего процесса, вы получаете возможность завершить выполнение на любом вложенном уровне с выходом без прерывания оболочки источника.

Ваш вызов для выхода будет жить, как вы пишете, в централизованной функции, вызываемой с любого уровня, когда принимается решение прекратить выполнение. Теперь перед завершением функции завершения работы необходимо записать текущую среду в механизм IPC в подходящем формате.

ipcscript.bash будет считывать настройки среды из механизма IPC и воспроизводить все настройки в процессе работы оболочки поиска.

7

return вы можете из считанного сценария оболочки. POSIX spec

Таким образом, в то время как вы не return из функции можно непосредственно получить то, что вы хотите, вы можете возврат из основного тела сценария, если ваша функция возвращает ненулевое значение (или какой-либо другой согласованные значения).

Например:

$ cat foo.sh 
f() { 
    echo in f "[email protected]" 
} 

e() { 
    return 2 
} 

f 1 
e 
f 2 
if ! e; then 
    return 
fi 
f 3 
$ . foo.sh 
in f 1 
in f 2 
+1

Спасибо за ваше решение. Я знаю об этом. Моя цель состоит в том, чтобы найти способ прекращения сценария с исходным кодом из одной из его функций, потому что дополнительная сложность, которую я действительно не могу включить в минимальный пример, делает очень желательным централизовать процедуру завершения в функции. – d3pd

+0

@ d3pd Вы не можете. Не без суб-оболочки. Это просто невозможно. Тем не менее, вы можете сохранить любую логику, которая вам нужна в функции. Вам просто нужно вызвать функцию в 'if' или' secure_environment || return' или что-то в этом роде. .... Вы можете * что-то сделать с помощью ловушки 'RETURN', проверяющей' $? 'Или любую другую переменную. –

2

Как об этом: Позвоните все через простую бандероль, здесь "ocall", который поддерживает глобальное состояние, здесь "STILL_OK"

STILL_OK=true 

ocall() { 
    if $STILL_OK 
    then 
     echo -- "[email protected]" # this is for debugging, you can delete this line 
     if "[email protected]" 
     then 
      true 
     else 
      STILL_OK=false 
     fi 
    fi 
} 

main(){ 
    ocall echo "starting test of environment..." 
    ocall ensure_environment 
    ocall echo "environment safe -- starting other procedures..." 
} 

ensure_environment(){ 
    if [ 1 == 1 ]; then 
     ocall echo "environment problemm -- terminating..." 
     # exit 1 # <-- terminates calling shell 
     return 1 # <-- returns from sourced script but leaves sourcing shell running 
    fi 
} 

ocall main 
+0

Для того чтобы '$ @' сохранить свои свойства, он должен в основном всегда иметь двойные кавычки. На самом деле это не имеет никакого значения, но для меня это немедленно вызывает тревогу новичков, поэтому вы можете исправить ее именно по этой причине. – tripleee

+0

Справа.Я избегаю bash, когда могу использовать ruby. Исправленный. –

+0

Nitpicking: 'если $ STILL_OK' является уязвимостью безопасности. Лучше использовать 'if [" $ STILL_OK "=" true "]', даже если это не так элегантно. В противном случае, если вы реализуете любой путь, ведущий к этой исходной строке, без установки 'STILL_OK', вы будете выполнять все, что в настоящее время установлено в вашей среде для' STILL_OK'. Наверное, ничего, кроме одного, никогда не знает. – Alfe

2

Это не возможное.

Если вы создаете скрипт, это (для рассматриваемых аспектов), как ввод каждой строки по одной в оболочке вызова (источника). Вы хотите оставить область действия (исходный сценарий), которая не существует, поэтому ее нельзя оставить.

Единственный способ, которым я могу думать, передавая выход, хотите вернуться к вызывающей функции и проверки для него:

main() { 
    echo "starting test of environment..." 
    [ "$(ensure_environment)" = "bailout" ] && return 
    echo "environment safe -- starting other procedures..." 
} 

ensure_environment() { 
    if [ 1 == 1 ]; then 
     echo "bailout" 
     return 
    fi 
} 

main 

То, что вы просите, также, как правило, не возможно и на других языках. Обычно каждая функция может только завершаться (возвращаясь), а не более широкую определенную область вне себя (например, сценарий, в котором он находится). An exception to this rule is exception handling с использованием try/catch или аналогичного.

Также рассмотрим следующее: если вы используете этот скрипт, функции оболочки станут известны в оболочке источника. Поэтому вы можете позвонить им позже. Тогда (опять же) нет окружающего пространства, функция может прекратиться.

0

Иногда я пишу сценарий, который имеет удобные функции, которые я хочу использовать вне сценария. В этом случае, если скрипт запущен, то он делает свое дело. Но если сценарий получен, он просто загружает некоторые функции в оболочку источника. Я использую эту форму:

#!/bin/bash 

# This function will be sourcable 
foo() { 
    echo hello world 
} 

# end if being sourced 
if [[ $0 == bash ]]; then 
    return 
fi 

# the rest of the script goes here 
Смежные вопросы