Различные способы выполнения сценария оболочки

44

Есть несколько способов выполнить скрипт, известные мне:

/path/to/script # using the path (absolute or relative)
. script        # using the . (dot)
source script   # using the `source` command

Чем больше это? Каковы различия между ними? Есть ли ситуации, в которых я должен использовать одно, а не другое?

phunehehe
источник
Приятно знать, благодаря вашему вопросу и ответам ниже, особенно Шон. Я хотел бы добавить кое-что, что было не очень очевидно для меня, пока я не провел несколько тестов. Включение «/» вторым способом, приведенным выше, переведет команду в режим 1 выше. То есть, хотя «./myscript.sh» следует за режимом 1, «. Myscript.sh» придерживается режима 2. Вы упомянули «использование пути (абсолютного или относительного)», но просто хотели сделать это очевидным.
Арун

Ответы:

32

Другой способ - вызвать интерпретатор и передать ему путь к скрипту:

/bin/sh /path/to/script

Точка и источник эквивалентны. (РЕДАКТИРОВАТЬ: нет, это не так: как указывает KeithB в комментарии к другому ответу, «.» Работает только в оболочках, связанных с bash, где «source» работает как в оболочках, связанных с bash, так и csh.) Он выполняет сценарий в -place (как будто вы скопировали и вставили скрипт прямо там). Это означает, что все функции и нелокальные переменные в скрипте остаются. Это также означает, что если скрипт вставит компакт-диск в каталог, вы все равно будете там, когда это будет сделано.

Другие способы запуска скрипта будут запускать его в своей собственной оболочке. Переменные в скрипте еще не живы, когда это сделано. Если скрипт изменил каталоги, то это не влияет на среду вызова.

/ path / to / script и / bin / sh script немного отличаются. Как правило, в начале скрипта есть «шебанг», который выглядит так:

#! /bin/bash

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

Например, сценарии Perl и сценарии Ruby начинаются с (соответственно):

#! /bin/perl

а также

#! /bin/ruby

Если вы запустите один из этих сценариев /bin/sh script, то они не будут работать вообще.

Ubuntu на самом деле не использует оболочку bash, но очень похожую, называемую dash. Скрипты, которые требуют bash, могут работать немного неправильно при вызове, /bin/sh scriptпотому что вы только что вызвали bash-скрипт, используя интерпретатор dash.

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

Еще один незначительный вариант: вы можете использовать любой из этих способов выполнения скрипта с помощью eval, так что вы можете иметь

eval sh script
eval script
eval . script

и так далее. На самом деле это ничего не меняет, но я подумал, что включу это для тщательности.

Шон Дж. Гофф
источник
6
Сказать «Ubuntu на самом деле не использует оболочку bash» неточно и технически неверно. Ubuntu делает использовать Баш оболочку, дело в том , что shсоответствует dash, но не к bash.
Фахим Митха
@Shawn В первом параграфе вы написали: «Он выполняет скрипт на месте (как если бы вы скопировали и вставили скрипт прямо там). Это означает, что все функции и нелокальные переменные в скрипте остаются». что вы подразумеваете под второй строкой здесь? Можете ли вы объяснить, пожалуйста.
Компьютерщик
@ Когда вы выполняете скрипт как дочерний процесс (обычным способом), все переменные и функции, которые он определяет (это среда), исчезают, когда процесс завершается. Если вы создаете сценарий, эти переменные и функции создаются в текущей среде; Когда сценарий завершается, изменения в среде остаются.
Шон Дж. Гофф
@ ShawnJ.Goff спасибо за разъяснения. +1.
Компьютерщик
Поворот сюжета: Bourne Shell (sh) принимает только точку, а не источник, поскольку она встроена в bash. pubs.opengroup.org/onlinepubs/9699919799/utilities/… Так что я бы сказал, что самый переносимый способ - это точка.
dezza
9

Большинство людей отлаживают сценарии оболочки, добавляя следующие сценарии отладки в сценарий:

set -x     # Print command traces before executing command.
set -v     # Prints shell input lines as they are read.
set -xv    # Or do both

Но это означает, что вам нужно открыть файл в редакторе (при условии, что у вас есть права на редактирование файла), добавить строку, например set -x, сохранить файл, а затем выполнить файл. Затем, когда вы закончите, вам нужно выполнить те же шаги и удалить и set -xт. Д. И т. Д. Это может быть утомительно.

Вместо того, чтобы делать все это, вы можете установить флаги отладки в командной строке:

$ bash -x ~/bin/ducks
+ du -cks -x dir1 dir2 dir3 file1 file2 file3
+ sort -n
+ tail .ducks
123 etc
424 bin
796 total



$ sh -xv ~/bin/ducks  
#!/usr/bin/env bash

# Find the disk hog
# Borrowed from http://oreilly.com/pub/h/15
...
...
Стефан Ласевский
источник
2
Соответствующий совет: у меня появилась привычка ставить emulate sh 2>/dev/nullверхнюю часть моих сценариев оболочки. При запуске с zsh это переводит его в POSIX-совместимый режим. При запуске с другими оболочками линия не имеет никакого эффекта. Тогда я могу запустить скрипт с zsh -x /path/to/script. Мне здесь нравится zsh, потому что он обеспечивает лучшие трассировки, чем bash или ksh.
Жиль "ТАК - перестань быть злым"
7

Шон Дж. Гофф сделал много хороших замечаний, но не включил всю историю:

Ubuntu на самом деле не использует оболочку bash, но очень похожую, называемую dash. Скрипты, которые требуют bash, могут работать немного неправильно при вызове с помощью /bin/shсценария, потому что вы только что вызвали сценарий bash, используя интерпретатор dash.

Многие системные скрипты (например, в init.d, в / etc и т. Д.) Имеют шебанг #!/bin/sh, но /bin/shна самом деле являются символической ссылкой на другую оболочку - в прежние времена /bin/bash, в настоящее время /bin/dash. Но когда один из них вызывается как /bin/sh, они ведут себя по-разному, то есть придерживаются режима совместимости с POSIX.

Как они это делают? Ну, они проверяют, как они были вызваны.

Может ли сам шеллскрипт проверить, как он вызывался, и делать разные вещи, в зависимости от этого? Да, оно может. То, как вы вызываете его, всегда может привести к разным результатам, но, конечно, это редко делается, чтобы вас раздражать. :)

Практическое правило. Если вы изучаете конкретную оболочку, например bash, и пишете команды из учебника по bash, вставьте #!/bin/bashзаголовок, но #!/bin/shтолько если не указано иное. Иначе ваши команды могут потерпеть неудачу. И если вы сами не написали скрипт, вызовите его напрямую ( ./foo.sh, bar/foo.sh), а не угадывайте оболочку ( sh foo.sh, sh bar/foo.sh). Шебанг должен вызывать правильную оболочку.

И вот два других вида вызова:

cat foo.sh | dash
dash < foo.sh
неизвестный пользователь
источник
5

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

Использование пути или его указание /bin/shсоздает новый процесс, в котором выполняются команды.

mouviciel
источник
2
sh script
bash script

Я размышляю, есть ли еще ...

.и sourceтакие же. После выполнения любые изменения окружения в scriptбудут сохранены. Обычно она используется для создания библиотеки Bash, поэтому ее можно повторно использовать во многих различных сценариях.

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

livibetter
источник
2
.работает только в sh / bash и связанных оболочках. sourceтакже работает в csh и связанных оболочках.
KeithB
1

Считается ли " userland exec " другим способом? Пользовательская область exec загружает код и заставляет его выполняться без использования системного вызова execve ().

Брюс Эдигер
источник
1
. ./filename
# ( dot space dot slash filename )

Запускает скрипт в текущей оболочке, когда каталог не находится в пути.

Стефан
источник
1

, и источник немного отличается в Zsh по крайней мере (это то, что я использую), потому что

source file

Работы, в то время как

. file

не нужно

. ./file
bollovan
источник