Spójrz na kod:
#!/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"
Ten kod służy do sprawdzania liczby terminali otwartych przez użytkownika na tym samym komputerze. Teraz jest zalogowanych dwóch użytkowników, powiedzmy xiy. Jestem aktualnie zalogowany jako y, a użytkownik x ma otwarte 3 terminale. Jeśli wykonam ten kod w y przy użyciu innych sposobów, jak wspomniano powyżej, wyniki są następujące:
$ ./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
Uwaga: przekazałem 1 i UID 1000 do wszystkich tych plików wykonywalnych.
Czy możesz teraz wyjaśnić różnice między nimi wszystkimi?
command-line
bash
scripts
Ramana Reddy
źródło
źródło
Odpowiedzi:
Jedyną istotną różnicą jest pozyskiwanie i wykonywanie skryptu.
source foo.sh
źródło, a wszystkie inne przykłady, które pokazujesz, są wykonywane. Bardziej szczegółowo:./file.sh
Spowoduje to wykonanie skryptu o nazwie
file.sh
znajdującej się w bieżącym katalogu (./
). Zwykle po uruchomieniucommand
powłoka przeszukuje katalogi w$PATH
poszukiwaniu pliku wykonywalnego o nazwiecommand
. Jeśli podasz pełną ścieżkę, taką jak/usr/bin/command
lub./command
, wówczas$PATH
jest ona ignorowana, a konkretny plik jest wykonywany.../file.sh
Jest to w zasadzie to samo,
./file.sh
z wyjątkiem tego, że zamiast szukać w bieżącym katalogufile.sh
, szuka w katalogu nadrzędnym (../
).sh file.sh
Jest to odpowiednik
sh ./file.sh
, jak powyżej, uruchomi skrypt wywoływanyfile.sh
w bieżącym katalogu. Różnica polega na tym, że jawnie uruchamiasz go za pomocąsh
powłoki. W systemach Ubuntu tak jestdash
i niebash
. Zwykle skrypty mają linię shebang, która daje program, który powinny być uruchomione jako. Wywoływanie ich za pomocą innego przesłania to. Na przykład:Ten skrypt po prostu wypisze nazwę powłoki używanej do jej uruchomienia. Zobaczmy, co zwraca, gdy zostanie wywołany na różne sposoby:
Wywołanie wywołania skryptu
shell script
zastąpi linię shebang (jeśli jest obecna) i uruchom skrypt z dowolną powłoką, którą mu powiesz.source file.sh
lub. file.sh
Zaskakująco nazywa się to pozyskiwaniem skryptu. Słowo kluczowe
source
to alias do wbudowanego.
polecenia powłoki . Jest to sposób na wykonanie skryptu w bieżącej powłoce. Zwykle, gdy skrypt jest wykonywany, jest uruchamiany we własnej powłoce, która jest inna niż bieżąca. Ilustrować:Teraz, jeśli ustawię zmienną
foo
na coś innego w powłoce nadrzędnej, a następnie uruchomię skrypt, skrypt wydrukuje inną wartośćfoo
(ponieważ jest on również ustawiony w skrypcie), ale wartośćfoo
w powłoce nadrzędnej pozostanie niezmieniona:Jednakże, jeśli skrytuję skrypt zamiast go uruchamiać, zostanie on uruchomiony w tej samej powłoce, więc wartość
foo
parametru nadrzędnego zostanie zmieniona:Tak więc pozyskiwanie jest używane w kilku przypadkach, w których skrypt ma wpływać na powłokę, z której go uruchamiasz. Zwykle służy do definiowania zmiennych powłoki i udostępniania ich po zakończeniu skryptu.
Mając to na uwadze, powodem, dla którego otrzymujesz różne odpowiedzi, jest przede wszystkim to, że twój skrypt nie robi tego, co myślisz. Zlicza ile razy
bash
pojawia się na wyjściups
. To nie jest liczba otwartych terminali , to liczba uruchomionych powłok (w rzeczywistości tak nie jest, ale to kolejna dyskusja). Aby to wyjaśnić, uprościłem nieco twój skrypt:I uruchom go na różne sposoby z otwartym tylko jednym terminalem:
Bezpośrednie uruchomienie,
./foo.sh
.Tutaj używasz linii shebang. Oznacza to, że skrypt jest wykonywany bezpośrednio przez cokolwiek tam ustawione. Wpływa to na sposób wyświetlania skryptu na wyjściu
ps
. Zamiast być wymienionym jakobash foo.sh
, zostanie pokazany tylko jako,foo.sh
co oznacza, żegrep
go przegapisz. W rzeczywistości działają 3 instancje bash: proces nadrzędny, bash uruchamiający skrypt i kolejna, która uruchamiaps
polecenie . To ostatnie jest ważne, uruchomienie polecenia z podstawieniem polecenia (`command`
lub$(command)
) powoduje uruchomienie kopii powłoki nadrzędnej i uruchomienie polecenia. Tutaj jednak żadne z nich nie jest pokazane ze względu na sposób, w jakips
pokazuje on swoje wyniki.Uruchamianie bezpośrednie za pomocą jawnej powłoki (bash)
Tutaj, ponieważ biegasz z
bash foo.sh
, wynikps
wyświetli siębash foo.sh
i zostanie policzony. Mamy tutaj proces nadrzędny,bash
uruchomienie skryptu i sklonowaną powłokę (uruchomienieps
), ponieważ terazps
pokaże każdy z nich, ponieważ twoje polecenie będzie zawierać słowobash
.Uruchamianie bezpośrednie za pomocą innej powłoki (
sh
)Jest inaczej, ponieważ uruchamiasz skrypt za pomocą
sh
i niebash
. Dlatego jedynąbash
instancją jest powłoka nadrzędna, w której uruchomiono skrypt.sh
Zamiast tego uruchamiane są wszystkie pozostałe wyżej wymienione powłoki .Pozyskiwanie (przez
.
lub tosource
samo)Jak wyjaśniono powyżej, pozyskiwanie skryptu powoduje, że działa on w tej samej powłoce co proces nadrzędny. Jednak oddzielna podpowłoka jest uruchamiana w celu uruchomienia
ps
polecenia, co daje całkowitą liczbę dwóch.Na koniec poprawnym sposobem zliczania uruchomionych procesów nie jest parsowanie,
ps
ale użyciepgrep
. Wszystkich tych problemów można by uniknąć, gdybyś po prostu biegłTak więc działającą wersją skryptu, która zawsze drukuje prawidłową liczbę, jest (zauważ brak zastępowania poleceń):
To zwróci 1 po źródle i 2 (ponieważ zostanie uruchomiony nowy bash, aby uruchomić skrypt) dla wszystkich innych sposobów uruchamiania. Nadal zwróci 1 po uruchomieniu,
sh
ponieważ proces potomny nie jestbash
.źródło
./foo.sh
działa w nowej powłoce, która nie jest kopią elementu nadrzędnego. Na przykład, jeśli ustawiszfoo="bar"
w swoim terminalu, a następnie uruchomisz skryptecho $foo
, który zostanie wykonany , otrzymasz pustą linię, ponieważ powłoka skryptu nie odziedziczy wartości zmiennej.pgrep
jest osobnym plikiem binarnym i tak, jest uruchamiany przez uruchamiany skrypt.