Единственное основное различие заключается в поиске и выполнении сценария. 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.