Любой способ выйти из скрипта bash, но не выходить из терминала

183

Когда я использую exitкоманду в сценарии оболочки, сценарий завершает работу терминала (приглашение). Есть ли способ завершить сценарий, а затем остаться в терминале?

run.shПредполагается, что мой сценарий будет выполнен напрямую из другого сценария.

РЕДАКТИРОВАТЬ: Чтобы быть более конкретным, есть два сценария run2.shкак

...
. run.sh
echo "place A"
...

и run.shкак

...
exit
...

когда я запускаю его . run2.sh, и если он exitдостигает кодовой линии run.sh, я хочу, чтобы он остановился в терминале и остался там. Но, используя exit, весь терминал закрывается.

PS: я пытался использовать return, но echoкодовая строка будет по-прежнему выполняется ....

Ричард
источник
5
Я действительно, действительно, действительно должен спросить: почему вы используете выход в сценарии с источником?
Игнасио Васкес-Абрамс
3
команда выхода не должна завершать сеанс терминала / вход в систему. если вы используете exit 0для завершения сценария после успеха, при запуске сценария ex: ./test.shвы должны увидеть выходные данные, но ваша консоль останется открытой.
Бен Эштон
Вы можете использовать shellкоманду, которая фактически открывает терминал оболочки. Однако мой собственный опыт показывает, что этого не происходит exit. Выход обычно возвращает управление родительскому скрипту.
Виллем Ван Онсем

Ответы:

258

«Проблема» в том, что вы используете источник, а не выполняете сценарий. Когда вы создаете исходный файл, его содержимое будет выполняться в текущей оболочке, а не порождать подоболочку. Так что все, включая выход, повлияет на текущую оболочку.

Вместо того, чтобы использовать exit, вы захотите использовать return.

Доминик Хоннеф
источник
2
Вот еще одно объяснение, которое я нашел полезным: askubuntu.com/a/53179/148337
колесное колесо
Хотя это правильно, это не очень хороший ответ. Он игнорирует, что вызывающий скрипт может объявлять переменные или функции, к которым этот вызывающий скрипт должен иметь доступ. Лучше объяснить, как установить код возврата, а затем обработать его в runs.sh@ruakh, и лучше ответить на этот вопрос.
MikeSchinkel
5
Что если функция является вложенным вызовом? то есть a вызывает b, b вызывает c, c хочет немедленно выйти из a и b.
Майкл
Источник почти такой же, как копировать вставить. Лучше использовать sh <script>или, bash <script>если кто-то хочет запустить скрипт и завершить его в какой-то момент
peterchaula
42

Да; Вы можете использовать returnвместо exit. Его основное назначение - возврат из функции оболочки, но если вы используете ее в sourceсценарии -d, он возвращается из этого сценария.

Как указано в п. 4.1 «Встроенные оболочки Bourne» Справочного руководства Bash :

     return [n]

Заставить функцию оболочки завершиться с возвращаемым значением n . Если n не указано, возвращаемое значение является состоянием выхода последней команды, выполненной в функции. Это также может быть использовано для прекращения выполнения скрипта, выполняемого встроенной .(или source), возвращающей либо n, либо статус завершения последней команды, выполненной в скрипте, в качестве статуса выхода скрипта. Любая команда, связанная с RETURNпрерыванием, выполняется до возобновления выполнения после функции или сценария. Статус возврата не равен нулю, если returnиспользуется вне функции, а не во время выполнения скрипта с помощью .или source.

ruakh
источник
3
returnможет использоваться ТОЛЬКО из функции. Если вы используете returnи выполняете его как сценарий оболочки (например sh run.sh), bash сообщит об ошибке - return: can only return 'из скрипта функции или источника'
Tzunghsing David Wong
@TzungsingDavidWong: Вы понимаете, что основная часть этого ответа - цитата из официального справочного руководства? И что сообщение об ошибке, которое вы цитируете, согласуется с этим ответом, а не с вашей претензией?
Руах
Я не согласен с тобой. Я просто хочу отметить, что returnэто не будет работать, если скрипт запускается как скрипт оболочки и не выполняется. (или source). Кстати, где я могу найти документ о source -d?
Цунхсинг Дэвид Вонг
9

Вместо запуска скрипта с помощью . run2.sh, вы можете запустить его с помощью sh run2.shилиbash run2.sh

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

Виорел Миреа
источник
Если так, то почему второй параметр sh "." run2.sh?
ЧАН
@ H0WARD Ты прав, я забыл удалить точки. Я отредактировал ответ сейчас.
Виорел Миреа
1
ОП явно указывает источник в качестве требования.
Джонатан Нойфельд
4

Вы можете добавить дополнительную команду выхода после оператора return / command, чтобы она работала как для выполнения сценария из командной строки, так и для получения из терминала.

Пример кода выхода в скрипте:

   if [ $# -lt 2 ]; then
     echo "Needs at least two arguments"
     return 1 2>/dev/null
     exit 1
   fi

Строка с exitкомандой не будет вызываться, когда вы создаете сценарий после returnкоманды.

При выполнении скрипта returnкоманда выдает ошибку. Итак, мы подавляем сообщение об ошибке, пересылая его /dev/null.

без имени
источник
3

На самом деле, я думаю, что вы можете быть смущены тем, как вы делаете run a script.

Например, если вы используете shдля запуска сценария, sh ./run2.shдаже если встроенный сценарий заканчивается exit, окно вашего терминала все равно останется.

Однако, если вы используете .или source, окно терминала также закроется и закроется, когда индекс завершится.

более подробно см. в чем разница между использованием shи source?

Карл Ли
источник
2

Это так же, как вы помещаете функцию запуска в ваш скрипт run2.sh. Вы используете код выхода внутри run, а исходный файл run2.sh - в bash tty. Если функция run дает возможность выйти из вашего скрипта и дает run2.sh возможность выйти из терминатора. Тогда у функции запуска есть возможность выйти из вашего теминатора.

    #! /bin/sh
    # use . run2.sh

    run()
    {
        echo "this is run"
        #return 0
        exit 0
    }

    echo "this is begin"
    run
    echo "this is end"

Во всяком случае, я согласен с Kaz это проблема дизайна.

Umae
источник
Из текста неясно, что пытается сделать этот ответ, но он, конечно, не решает рассматриваемый вопрос.
Луис де Соуза
2

У меня была та же проблема, и из приведенных выше ответов, а также из того, что я понял, в конечном итоге мне удалось:

  1. Есть строка Шебанга, которая вызывает предполагаемый сценарий, например,

    #!/bin/bashиспользует bashдля выполнения скрипта

У меня есть сценарии с обоими видами Шебанга. Из-за этого использование shили .было ненадежным, так как это приводило к неправильному выполнению (например, когда сценарий вылетает не полностью)

Таким образом, ответ был

    • Убедитесь, что в скрипте есть шебанг, чтобы не было никаких сомнений относительно его предполагаемого обработчика.
    • chmod .sh файл, чтобы он мог быть выполнен. (chmod +x file.sh)
    • Вызывать его напрямую без каких-либо shили.

      (./myscript.sh)

Надеюсь, что это помогает кому-то с похожим вопросом или проблемой.

gk_2000
источник
1

Я думаю, что это происходит, потому что вы запускаете его в исходном режиме с точкой

. myscript.sh

Вы должны запустить это в подоболочке:

/full/path/to/script/myscript.sh

'источник' http://ss64.com/bash/source.html

JBoy
источник
Вам не нужен полный путь к сценарию, если . myscript.shработает. Самое большее, вам может понадобиться ./myscript.sh.
Альваро Гонсалес,
1

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

Вот соответствующий совет, если вам нужен скрипт, который должен держать сеанс открытым, независимо от того, получен он или нет.

Следующий пример может быть запущен напрямую как foo.shили из источника . foo.sh/ source foo.sh. В любом случае он будет держать сеанс открытым после «выхода». $@Строка передаются так , что функция имеет доступ к аргументам внешнего скрипта.

#!/bin/sh
foo(){
    read -p "Would you like to XYZ? (Y/N): " response;
    [ $response != 'y' ] && return 1;
    echo "XYZ complete (args $@).";
    return 0;
    echo "This line will never execute.";
}
foo "$@";

Терминальный результат:

$ foo.sh
$ Вы хотите XYZ? (Y / N): n
$. foo.sh
$ Хотели бы вы XYZ? (Д / Н): n
$ |
(окно терминала остается открытым и принимает дополнительный ввод)

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

Beejor
источник
0

если в вашем эмуляторе терминала его нет, -holdвы можете выполнить дезинфекцию исходного сценария и удерживать терминал с помощью:

#!/bin/sh
sed "s/exit/return/g" script >/tmp/script
. /tmp/script
read

в противном случае вы можете использовать $TERM -hold -e script

technosaurus
источник
0

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

coder23
источник
0

Для того, чтобы написать сценарий , который является безопасным для запуска или как сценарий оболочки или источников в виде файла гс, сценарий может проверить и сравнить $0и $BASH_SOURCEи определить, exitможно безопасно использовать.

Вот короткий фрагмент кода для этого

[ "X$(basename $0)" = "X$(basename $BASH_SOURCE)" ] && \
    echo "***** executing $name_src as a shell script *****" || \
    echo "..... sourcing $name_src ....."
Цунхсинг Дэвид Вонг
источник
-1

1) выход 0 выйдет из скрипта, если он будет успешным.

2) выход 1 выйдет из скрипта, если он окажется неудачным.

Вы можете попробовать эти два выше на основе вашего требования.

Тея
источник