Różne sposoby wykonywania skryptu powłoki

44

Istnieje kilka sposobów wykonania skryptu, z których znam:

/path/to/script # using the path (absolute or relative)
. script        # using the . (dot)
source script   # using the `source` command

Czy to więcej? Jakie są między nimi różnice? Czy są sytuacje, w których muszę korzystać z jednej, a nie drugiej?

phunehehe
źródło
Wspaniale wiedzieć, dzięki twoim pytaniom i odpowiedziom poniżej, zwłaszcza Shawnowi. Chciałbym dodać coś, co nie było dla mnie zbyt widoczne, dopóki nie przeprowadziłem kilku testów. Włączenie znaku „/” w drugi sposób powyżej spowoduje przejście polecenia do trybu 1 powyżej. To znaczy, podczas gdy „./myscript.sh” podąża za trybem 1, „. Myscript.sh” trzyma się trybu 2. Wspomniałeś o „użyciu ścieżki (bezwzględnej lub względnej)”, ale chciałeś tylko to pokazać.
Arun

Odpowiedzi:

32

Innym sposobem jest wywołanie interpretera i przekazanie mu ścieżki do skryptu:

/bin/sh /path/to/script

Kropka i źródło są równoważne. (EDYCJA: nie, nie są: jak KeithB wskazuje w komentarzu do innej odpowiedzi: „.” Działa tylko w powłokach związanych z bash, gdzie „źródło” działa zarówno w powłokach związanych z bash, jak i csh.) Wykonuje skrypt w -place (tak jakbyś skopiował i wkleił skrypt tutaj). Oznacza to, że pozostaną wszystkie funkcje i zmienne nielokalne w skrypcie. Oznacza to również, że jeśli skrypt włoży płytę CD do katalogu, to po zakończeniu będziesz tam nadal.

Inne sposoby uruchamiania skryptu powodują uruchomienie go we własnej podpowłoce. Zmienne w skrypcie nie są jeszcze aktywne, gdy jest gotowe. Jeśli skrypt zmieni katalogi, nie wpłynie to na środowisko wywołujące.

Skrypty / path / to / script i / bin / sh różnią się nieco. Zazwyczaj skrypt ma „shebang” na początku, który wygląda następująco:

#! /bin/bash

To jest ścieżka do interpretera skryptów. Jeśli określa innego interpretera niż ty, kiedy go wykonujesz, może on zachowywać się inaczej (lub w ogóle nie działać).

Na przykład skrypty Perla i Ruby zaczynają się (odpowiednio):

#! /bin/perl

i

#! /bin/ruby

Jeśli wykonasz jeden z tych skryptów przez uruchomienie /bin/sh script, to w ogóle nie będą działać.

Ubuntu w rzeczywistości nie używa powłoki bash, ale bardzo podobną o nazwie dash. Skrypty wymagające bash mogą działać nieco niepoprawnie po wywołaniu, /bin/sh scriptponieważ właśnie wywołałeś skrypt bash za pomocą interpretera myślników.

Inną niewielką różnicą między bezpośrednim wywołaniem skryptu a przekazaniem ścieżki skryptu do interpretera jest to, że skrypt musi być oznaczony jako wykonywalny, aby uruchomić go bezpośrednio, ale nie należy go uruchamiać, przekazując ścieżkę do interpretera.

Kolejna drobna odmiana: możesz poprzedzić dowolny z tych sposobów wykonania skryptu eval, więc możesz to zrobić

eval sh script
eval script
eval . script

i tak dalej. W rzeczywistości nic to nie zmienia, ale pomyślałem, że dodam to dla dokładności.

Shawn J. Goff
źródło
6
Mówienie „Ubuntu faktycznie nie używa powłoki bash” jest nieprecyzyjne i technicznie niepoprawne. Ubuntu nie używać powłoki bash, chodzi o to, że shodpowiada dash, ale nie bash.
Faheem Mitha
@Shawn W pierwszej części napisałeś: „Wykonuje skrypt w miejscu (tak jakbyś skopiował i wkleił skrypt w tym miejscu). Oznacza to, że wszystkie funkcje i zmienne nielokalne w skrypcie pozostają”. co rozumiesz przez drugą linię tutaj? Czy możesz to wyjaśnić?
Geek
@Geek po uruchomieniu skryptu jako procesu potomnego (normalny sposób) wszelkie zmienne i funkcje, które definiuje (jego środowisko) znikają po zakończeniu procesu. Jeśli skrypt zostanie źródłowy, te zmienne i funkcje zostaną utworzone w bieżącym środowisku; po zakończeniu skryptu zmiany w środowisku pozostają.
Shawn J. Goff,
@ ShawnJ.Goff dzięki za wyjaśnienie. +1.
Geek
Plot-twist: Bourne Shell (sh) akceptuje tylko kropki - nie źródła, ponieważ jest to wbudowane bash. pubs.opengroup.org/onlinepubs/9699919799/utilities/... Więc powiedziałbym, że najbardziej przenośnym sposobem jest kropka.
dezza
9

Większość osób debuguje skrypty powłoki, dodając do skryptu następujące flagi debugowania :

set -x     # Print command traces before executing command.
set -v     # Prints shell input lines as they are read.
set -xv    # Or do both

Ale to oznacza, że ​​musisz otworzyć plik za pomocą edytora (zakładając, że masz uprawnienia do edycji pliku), dodając wiersz set -x, zapisz plik, a następnie uruchom plik. Następnie, gdy skończysz, musisz wykonać te same kroki i usunąć set -xitp. Itp. Może to być uciążliwe.

Zamiast robić to wszystko, możesz ustawić flagi debugowania w wierszu poleceń:

$ bash -x ~/bin/ducks
+ du -cks -x dir1 dir2 dir3 file1 file2 file3
+ sort -n
+ tail .ducks
123 etc
424 bin
796 total



$ sh -xv ~/bin/ducks  
#!/usr/bin/env bash

# Find the disk hog
# Borrowed from http://oreilly.com/pub/h/15
...
...
Stefan Lasiewski
źródło
2
Powiązana wskazówka: nauczyłem się umieszczać emulate sh 2>/dev/nullna szczycie moich skryptów powłoki. Po uruchomieniu z zsh powoduje to przejście do trybu zgodnego z POSIX. Podczas uruchamiania z innymi powłokami linia nie ma wpływu. Potem mogę uruchomić skrypt zsh -x /path/to/script. Podoba mi się tutaj zsh, ponieważ zapewnia lepsze ślady niż bash lub ksh.
Gilles 'SO - przestań być zły'
7

Shawn J. Goff przedstawił wiele dobrych punktów, ale nie uwzględnił całej historii:

Ubuntu w rzeczywistości nie używa powłoki bash, ale bardzo podobną o nazwie dash. Skrypty wymagające bash mogą działać nieco niepoprawnie po wywołaniu przez wykonanie /bin/shskryptu, ponieważ właśnie wywołałeś skrypt bash za pomocą interpretera dash.

Wiele skryptów systemowych (jak w init.d, w / etc itd.) Ma shebang #!/bin/sh, ale /bin/shw rzeczywistości jest symbolicznym łącznikiem z inną powłoką - w dawnych czasach /bin/bash, obecnie /bin/dash. Ale gdy jeden z nich zostanie wywołany jako /bin/sh, zachowują się inaczej, tzn. Trzymają się trybu zgodności z POSIX.

Jak oni to robią? Cóż, sprawdzają, jak zostały przywołane.

Czy sam skrypt Shells może przetestować sposób jego wywołania i wykonywać różne czynności, zależnie od tego? Tak, może. Sposób, w jaki ją wywołujesz, zawsze może prowadzić do różnych rezultatów, ale oczywiście rzadko robi się to, aby cię drażnić. :)

Ogólna zasada: jeśli uczysz się konkretnej powłoki, takiej jak bash, i piszesz polecenia z samouczka bash, nie umieszczaj #!/bin/bashnagłówka #!/bin/sh, chyba że zaznaczono inaczej. W przeciwnym razie twoje polecenia mogą zawieść. A jeśli sam nie napisałeś skryptu, wywołaj go bezpośrednio ( ./foo.sh, bar/foo.sh) zamiast zgadywania powłoki ( sh foo.sh, sh bar/foo.sh). Shebang powinien wywoływać odpowiednią skorupę.

A oto dwa inne rodzaje wywołań:

cat foo.sh | dash
dash < foo.sh
nieznany użytkownik
źródło
5

.i sourcesą równoważne, ponieważ nie spawnują podprocesu, ale wykonują polecenia w bieżącej powłoce. Jest to ważne, gdy skrypt ustawia zmienne środowiskowe lub zmienia bieżący katalog roboczy.

Użycie ścieżki lub nadanie jej do /bin/shutworzenia nowego procesu, w którym wykonywane są polecenia.

mouviciel
źródło
2
sh script
bash script

Zastanawiam się, czy jest więcej ...

.i sourcesą takie same. Po wykonaniu zmiany środowiska scriptzostaną zachowane. Zwykle byłby używany do pozyskiwania biblioteki Bash, więc biblioteka może być ponownie używana w wielu różnych skryptach.

Jest to również dobry sposób na utrzymanie bieżącego katalogu. Jeśli zmienisz katalog w skrypcie, nie zostanie on zastosowany w powłoce, którą wykonujesz. Ale jeśli go uruchomisz, po zakończeniu skryptu zostanie zachowany bieżący katalog.

livibetter
źródło
2
.działa tylko w sh / bash i pokrewnych powłokach. sourcedziała również w csh i pokrewnych powłokach.
KeithB
1

Czy „ userland exec ” liczy się inaczej? Userland exec ładuje kod i uruchamia go bez użycia wywołania systemowego execve ().

Bruce Ediger
źródło
1
. ./filename
# ( dot space dot slash filename )

Uruchamia skrypt w bieżącej powłoce, gdy katalog nie znajduje się na ścieżce.

Stefan
źródło
1

. i źródło różnią się przynajmniej trochę w Zsh (tego używam), ponieważ

source file

Działa, póki

. file

nie potrzebuje

. ./file
Bollovan
źródło