Посмотрите на код:
#!/bin/bash
read -p "Eneter 1 for UID and 2 for LOGNAME" choice
if [ $choice -eq 1 ]
then
read -p "Enter UID: " uid
logname=`cat /etc/passwd | grep $uid | cut -f1 -d:`
else
read -p "Enter Logname: " logname
fi
not=`ps -au$logname | grep -c bash`
echo "The number of terminals opened by $logname are $not"
Этот код используется для определения количества терминалов, открытых пользователем на одном ПК. Теперь в систему вошли два пользователя, скажем, x и y. В настоящее время я вошел в систему как y, и у пользователя x открыто 3 терминала. Если я выполню этот код в y, используя различные способы, как упомянуто выше, результаты:
$ ./file.sh
The number of terminals opened by x are 3
$ bash file.sh
The number of terminals opened by x are 5
$ sh file.sh
The number of terminals opened by x are 3
$ source file.sh
The number of terminals opened by x are 4
$ . ./file.sh
The number of terminals opened by x are 4
Примечание: я передал 1 и uid 1000 всем этим исполняемым файлам.
Теперь не могли бы вы объяснить различия между всеми этими?
command-line
bash
scripts
Рамана Редди
источник
источник
Ответы:
Единственное основное различие заключается в поиске и выполнении сценария.
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, в которой указана программа, с которой они должны работать. Вызов их другим переопределяет это. Например:Этот скрипт просто напечатает имя оболочки, используемой для его запуска. Давайте посмотрим, что он возвращает, когда вызывается по-разному:
Таким образом, вызов вызова сценария с
shell script
переопределением строки shebang (если присутствует) и выполнением сценария с любой оболочкой, о которой вы говорите.source file.sh
или. file.sh
Это на удивление называется поиском сценария. Ключевое слово
source
является псевдонимом встроенной.
команды оболочки . Это способ выполнения скрипта в текущей оболочке. Обычно, когда скрипт выполняется, он запускается в своей собственной оболочке, которая отличается от текущей. Проиллюстрировать:Теперь, если я установлю переменную
foo
на что-то другое в родительской оболочке, а затем запустлю сценарий, сценарий напечатает другое значениеfoo
(потому что оно также установлено в сценарии), но значениеfoo
в родительской оболочке останется неизменным:Однако, если я запишу сценарий вместо его выполнения, он будет запущен в той же оболочке, поэтому значение
foo
в родительском будет изменено:Итак, использование источников используется в тех немногих случаях, когда вы хотите, чтобы скрипт воздействовал на оболочку, из которой вы его запускаете. Обычно он используется для определения переменных оболочки и их доступности после завершения скрипта.
Учитывая все это, причина, по которой вы получаете разные ответы, заключается, прежде всего, в том, что ваш сценарий не делает то, что вы думаете, он делает. Подсчитывает количество раз, которое
bash
появляется на выходеps
. Это не количество открытых терминалов , это количество работающих оболочек (на самом деле, это даже не это, но это другое обсуждение). Чтобы уточнить, я немного упростил ваш скрипт к этому:И запускайте его различными способами, когда открыт только один терминал:
Прямой запуск
./foo.sh
.Здесь вы используете линию Шебанга. Это означает, что сценарий выполняется напрямую тем, что там установлено. Это влияет на способ отображения сценария в выходных данных
ps
. Вместо того, чтобы быть перечисленным какbash foo.sh
, это будет только показано как,foo.sh
что означает, что Выgrep
пропустите это. На самом деле запущено 3 экземпляра bash: родительский процесс, bash, выполняющий скрипт, и еще один, выполняющийps
команду . Последнее важно, запуск команды с подстановкой команд (`command`
или$(command)
) приводит к тому, что запускается родительская оболочка, которая запускает команду. Здесь, однако, ни один из них не показан из-за способа, которыйps
показывает его вывод.Прямой запуск с явной (bash) оболочкой
Здесь, поскольку вы работаете с
bash foo.sh
, результатps
будет показанbash foo.sh
и будет подсчитан. Итак, здесь у нас есть родительский процесс,bash
запуск сценария и клонированная оболочка (запускps
), потому что теперьps
будет показан каждый из них, потому что ваша команда будет включать словоbash
.Прямой запуск с другой оболочкой (
sh
)Это отличается, потому что вы запускаете скрипт с,
sh
а не сbash
. Поэтому единственнымbash
экземпляром является родительская оболочка, в которой вы запустили свой скрипт. Все остальные оболочки, упомянутые выше, находятся в веденииsh
.Sourcing (либо,
.
либоsource
, то же самое)Как я объяснил выше, использование сценария приводит к тому, что он запускается в той же оболочке, что и родительский процесс. Тем не менее, запускается отдельная подоболочка для запуска
ps
команды, и в результате получается всего два.И последнее замечание: правильный способ подсчета запущенных процессов - не анализировать,
ps
а использоватьpgrep
. Все эти проблемы можно было бы избежать, если бы вы просто побежалиИтак, рабочая версия вашего скрипта, которая всегда печатает правильное число (обратите внимание на отсутствие подстановки команд):
Это вернет 1 при получении и 2 (потому что для запуска скрипта будет запущен новый bash) для всех других способов запуска. Он все равно вернет 1 при запуске с,
sh
так как дочерний процесс - нетbash
.источник
./foo.sh
запускается в новой оболочке, которая не является копией родительского. Например, если вы установитеfoo="bar"
в своем терминале, а затем запустите скрипт, который выполняетсяecho $foo
, вы получите пустую строку, поскольку оболочка скрипта не унаследует значение переменной.pgrep
это отдельный двоичный файл, и да, он запускается сценарием, который вы запускаете.