Єдина основна відмінність - між джерелом пошуку та виконанням сценарію. 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
Тут ви використовуєте лінію shebang. Це означає, що сценарій виконується безпосередньо тим, що там встановлено. Це впливає на те, як скрипт відображається на виході 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 (тому що для запуску сценарію буде запущений новий баш) для всіх інших способів запуску. Він все одно поверне 1 при запуску, shоскільки дочірній процес не є bash.