Единственное основное различие заключается в поиске и выполнении сценария. source foo.sh
будет исходить из него и все остальные примеры, которые вы показываете, выполняются. Подробнее:
./file.sh
Это выполнит скрипт с именем, file.sh
который находится в текущем каталоге ( ./
). Обычно, когда вы запускаете command
, оболочка просматривает каталоги в вашем $PATH
файле для поиска исполняемого файла command
. Если вы укажете полный путь, например, /usr/bin/command
или ./command
, то этот параметр $PATH
игнорируется, и этот конкретный файл выполняется.
../file.sh
Это в основном то же самое, ./file.sh
за исключением того, что вместо поиска в текущем каталоге file.sh
он ищет в родительском каталоге ( ../
).
sh file.sh
Это эквивалентно тому sh ./file.sh
, как указано выше, он будет запускать скрипт, вызываемый file.sh
в текущем каталоге. Разница в том, что вы явно запускаете его с sh
оболочкой. На системах Ubuntu, то есть dash
и нет bash
. Обычно в скриптах есть строка shebang, в которой указана программа, с которой они должны работать. Вызов их другим переопределяет это. Например:
$ cat foo.sh
#!/bin/bash
## The above is the shebang line, it points to bash
ps h -p $$ -o args='' | cut -f1 -d' ' ## This will print the name of the shell
Этот скрипт просто напечатает имя оболочки, используемой для его запуска. Давайте посмотрим, что он возвращает, когда вызывается по-разному:
$ bash foo.sh
bash
$ sh foo.sh
sh
$ zsh foo.sh
zsh
Таким образом, вызов вызова сценария с shell script
переопределением строки shebang (если присутствует) и выполнением сценария с любой оболочкой, о которой вы говорите.
source file.sh
или . file.sh
Это на удивление называется поиском сценария. Ключевое слово source
является псевдонимом встроенной .
команды оболочки . Это способ выполнения скрипта в текущей оболочке. Обычно, когда скрипт выполняется, он запускается в своей собственной оболочке, которая отличается от текущей. Проиллюстрировать:
$ cat foo.sh
#!/bin/bash
foo="Script"
echo "Foo (script) is $foo"
Теперь, если я установлю переменную foo
на что-то другое в родительской оболочке, а затем запустлю сценарий, сценарий напечатает другое значение foo
(потому что оно также установлено в сценарии), но значение foo
в родительской оболочке останется неизменным:
$ foo="Parent"
$ bash foo.sh
Foo (script) is Script ## This is the value from the script's shell
$ echo "$foo"
Parent ## The value in the parent shell is unchanged
Однако, если я запишу сценарий вместо его выполнения, он будет запущен в той же оболочке, поэтому значение foo
в родительском будет изменено:
$ source ./foo.sh
Foo (script) is Script ## The script's foo
$ echo "$foo"
Script ## Because the script was sourced,
## the value in the parent shell has changed
Итак, использование источников используется в тех немногих случаях, когда вы хотите, чтобы скрипт воздействовал на оболочку, из которой вы его запускаете. Обычно он используется для определения переменных оболочки и их доступности после завершения скрипта.
Учитывая все это, причина, по которой вы получаете разные ответы, заключается, прежде всего, в том, что ваш сценарий не делает то, что вы думаете, он делает. Подсчитывает количество раз, которое bash
появляется на выходе ps
. Это не количество открытых терминалов , это количество работающих оболочек (на самом деле, это даже не это, но это другое обсуждение). Чтобы уточнить, я немного упростил ваш скрипт к этому:
#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo "The number of shells opened by $logname is $not"
И запускайте его различными способами, когда открыт только один терминал:
Прямой запуск ./foo.sh
.
$ ./foo.sh
The number of shells opened by terdon is 1
Здесь вы используете линию Шебанга. Это означает, что сценарий выполняется напрямую тем, что там установлено. Это влияет на способ отображения сценария в выходных данных ps
. Вместо того, чтобы быть перечисленным как bash foo.sh
, это будет только показано как, foo.sh
что означает, что Вы grep
пропустите это. На самом деле запущено 3 экземпляра bash: родительский процесс, bash, выполняющий скрипт, и еще один, выполняющий ps
команду . Последнее важно, запуск команды с подстановкой команд ( `command`
или $(command)
) приводит к тому, что запускается родительская оболочка, которая запускает команду. Здесь, однако, ни один из них не показан из-за способа, который ps
показывает его вывод.
Прямой запуск с явной (bash) оболочкой
$ bash foo.sh
The number of shells opened by terdon is 3
Здесь, поскольку вы работаете с bash foo.sh
, результат ps
будет показан bash foo.sh
и будет подсчитан. Итак, здесь у нас есть родительский процесс, bash
запуск сценария и клонированная оболочка (запуск ps
), потому что теперь ps
будет показан каждый из них, потому что ваша команда будет включать слово bash
.
Прямой запуск с другой оболочкой ( sh
)
$ sh foo.sh
The number of shells opened by terdon is 1
Это отличается, потому что вы запускаете скрипт с, sh
а не с bash
. Поэтому единственным bash
экземпляром является родительская оболочка, в которой вы запустили свой скрипт. Все остальные оболочки, упомянутые выше, находятся в ведении sh
.
Sourcing (либо, .
либо source
, то же самое)
$ . ./foo.sh
The number of shells opened by terdon is 2
Как я объяснил выше, использование сценария приводит к тому, что он запускается в той же оболочке, что и родительский процесс. Тем не менее, запускается отдельная подоболочка для запуска ps
команды, и в результате получается всего два.
И последнее замечание: правильный способ подсчета запущенных процессов - не анализировать, ps
а использовать pgrep
. Все эти проблемы можно было бы избежать, если бы вы просто побежали
pgrep -cu terdon bash
Итак, рабочая версия вашего скрипта, которая всегда печатает правильное число (обратите внимание на отсутствие подстановки команд):
#!/usr/bin/env bash
user="terdon"
printf "Open shells:"
pgrep -cu "$user" bash
Это вернет 1 при получении и 2 (потому что для запуска скрипта будет запущен новый bash) для всех других способов запуска. Он все равно вернет 1 при запуске с, sh
так как дочерний процесс - нет bash
.