W pierwszych skryptach, które pisaliśmy, nie ma tego konstruktu, ale może on być przydatny w skryptach Perla na systemach uniksowych takich jak Linux czy Mac OSX.

Nie jest tak naprawdę wymagany, możesz po prostu ominąć ten artykuł i wrócić później, kiedy będziesz chciał zrozumieć co znaczy #!/usr/bin/perl na początku wielu skryptów Perla.

Zanim przejdę do szczegółów, pozwól mi powiedzieć, że tak linia jest także nazywana she-bang, Shebang lub sh-bang, a także innymi nazwami.

Pierwszy program, którego zwykle uczą się ludzie, to zwykle "Hello world". Oto skrypt:

use strict;
use warnings;
 
print "Hello World\n";

Możemy go zapisać do pliku nazwanego hello.pl, otworzyć terminal (lub Cmd pod Windows). cd do katalogu gdzie zapisaliśmy plik i uruchomić skrypt przez wpisanie perl hello.pl.

To znaczy, uruchamiamy Perla i mówimy mu, by wykonał nasz skrypt.

Czy byłoby możliwe wykonanie skryptu bez wcześniejszego uruchomienie perla? Czy byłoby możliwe po prostu uruchomić hello.pl?

W systemach uniksowych jest to całkiem proste. Pod Windows jest to całkiem inna historia i zostanie poruszona oddzielnie.

hash-bang w systemach uniksowych

Spróbujmy uruchomić skrypt:

$ hello.pl
-bash: hello.pl: command not found

Nasze środowisko nie może znaleźć skryptu.

Co jeśli podamy ścieżkę do skryptu (który jest i tak w bieżącym katalogu):

$ ./hello.pl
-bash: ./hello.pl: Permission denied

Teraz już znajduje skrypt, ale nie ma uprawnień do jego uruchomienia.

Na Linuksie czy Mac OSX, czy każdym innym systemie uniksowym możemy uczynić skrypt "wykonywalnym" przez ustawienie bitu w standardowych atrybutach pliku w tak zwanej tablicy inode'ów. Można to łatwo zrobić używając polecenia chmod. Użyjemy chmod u+x hello.pl:

Najpierw użyjemy polecenia ls -l powłoki Uniksa aby zobaczyć sytuację przed, następnie użyjemy chmod do zmiany uprawnień, a na końcu sprawdzimy sytuację po tej operacji. Część u+x operacji mówi chmod aby dodał prawa wykonania (x) dla użytkownika (u), który jest właścicielem tego pliku, ale dla nikogo innego. (chmod +x hello.pl przyznałoby prawa wykonania każdemu w systemie.)

$ ls -l hello.pl
-rw-r--r--  1 gabor  staff  50 Apr 21 10:11 hello.pl

$ chmod u+x hello.pl 

$ ls -l hello.pl 
-rwxr--r--  1 gabor  staff  50 Apr 21 10:11 hello.pl

Zwróć uwagę na dodatkowy x jako czwarty znak odpowiedzi.

Spróbujmy uruchomić skrypt ponownie:

$ ./hello.pl 
./hello.pl: line 1: use: command not found
./hello.pl: line 2: use: command not found
./hello.pl: line 4: print: command not found

Znacznie lepiej :)

Teraz możemy już uruchomić skrypt, ale nie robi on tego, co chcemy. W rzeczywistości narzeka, że nie może znaleźć poleceń 'use' ani 'print'. To co się tu wydarzyło to to, że powłoka, której używamy (prawdopodobnie bash) próbował interpretować polecenia w pliku, ale nie znalazł takich poleceń jak use czy print w Linuksie/Uniksie. W jakiś sposób musimy powiedzieć powłoce, że jest to skrypt perlowy. Służy do tego hash-bang.

Jeśli wyedytujemy plik i dodamy

#!/usr/bin/perl

jako pierwszą linię skryptu i bez spacji, a następnie spróbujemy uruchomić skrypt ponownie:

$ ./hello.pl 
Hello World

działa już jak oczekujemy.

Jednakże, jeśli spróbujemy uruchoić go bez ./, nadal nie będzie mógł go znaleźć:

$ hello.pl
-bash: hello.pl: command not found

W celu rozwiązania tego musimy zmienić zmienną środowiskową PATH. Jako, że skupiamy się głównie na linii hash-bang, nie chcę wchodzić w dalsze dokładnie wyjaśnienia, więc pozwól, że po prostu podam Ci polecenie:

$ PATH=$PATH:$(pwd)

dołączające bieżący katalog do listy katalogów w zmiennej środowiskowej PATH. Gdy to zrobimy, możemy teraz uruchomić:

$ hello.pl
Hello World

Jak działa linia hash-bang?

Dodaliśmy #!/usr/bin/perl jako pierwszą linię naszego skryptu:

Kiedy uruchamiamy skrypt, uruchamiamy go w środowisku naszej bieżącej powłoki. Dla większości ludzi pod Linuksem/Uniksem będzie to Bash. Bash przeczyta pierwszą linię skryptu. Jeśli zaczyna się ona od hasha i wykrzyknikiem (hash-bang) #!, wówczas Bash uruchomi aplikację, której ścieżka jest w linii hash-bang (w naszym przypadku /usr/bin/perl, co jest standardową lokalizacją kompilatora-interpretera perl na większości współczesnych systemów Uniksowych).

Linia hash-bang zawiera ścieżkę do kompilatora-interpretera Perla.

Jeśli pierwsza linia nie zaczyna się od #!, jak miało to miejsce w naszym pierwotnym skrypcie, Bash będzie przypuszczał, że jest to skrypt napisany w Bashu i będzie próbował zrozumieć go samodzielnie. Właśnie to powodowało błędy.

Alternatywne linie hash-bang z użyciem env

Choć używaliśmy #!/usr/bin/perl jako naszą linię hash-bang, może ona wyglądać inaczej. Na przykłąd jeśli zainstalowaliśmy inną wersję perla w innej lokalizacji i chcemy, by nasze skrypty z niej korzystały, możemy podać ścieżkę do tej wersji perla. Na przykład #!/opt/perl-5.18.2/bin/perl.

Przewaga ustawienia hash-bang (i włączenia bitu wykonywalności) jest taka, że użytkownik nie musi wiedzieć, że skrypt jest napisany w Perlu i jeśli masz wiele instancji Perla w swoim systemie, linia hash-bang może zostać użyta do wybrania, która wersja perla ma być użyta. Będzie ona taka sama dla wszytkich ludzi na danej maszynie. Wada jest taka, że wersja perla wymieniona w linii hash-bang jest wykorzystywana tylko, gdy skrypt jest uruchamiany jako ./hello.pl lub jako hello.pl. Jeśli zostanie uruchomiony jako perl hello.pl, wykorzysta wersję perla, która zostanie znaleziona jako pierwsza w katalogach wymienionych w PATH. I może ona być inna, niż wersja perla z linii hash-bang.

Z tego powodu, na współczesnych systemach Linux/Unix, ludzie mogą preferować użycie #!/usr/bin/env perl jako linii hash-bang. Kiedy Bash zobaczy taką linię, najpierw wykona polecenie env przekazując perl do niego. env znajdzie pierwszego perla w katalogach PATH i uruchomi go. Zatem jeśli mamy #!/usr/bin/env perl w naszym skrypcie, zawsze będzie on korzystac z pierwszego perla w naszym PATH. Zarówno, jeśli zostanie wywołany jako ./hello.pl, jak i gdy zostanie wywołany przez perl hello.pl. Ma to także wadę, ponieważ polega na właściwym ustawieniu zmiennej PATH przez użytkowników.

Oto tabela, która próbuje wyjaśnić 4 przypadki:

  hash-bang                  Który perl jest używany do uruchomienia skryptu przy wywołaniu:
                             ./hello.pl                    perl hello.pl

  /usr/bin/perl              /usr/bin/perl                 pierwszy perl w PATH
  /usr/bin/env perl          pierwszy perl w PATH          pierwszy perl w PATH

Flagi w linii hash-bang

W linii hash-bang, po ścieżce do perla, możemy przekazać perlowi flagi wiersza poleceń. Prawdopodobnie zobaczysz wiele skrytpów zaczynających się od #!/usr/bin/perl -w czy może #!/usr/bin/env perl -w. -w w tym hash-bangu włącza ostrzeżenia. Jest to zupełnie podobne do tego, co robi use warnings, ale jest użyte w starym stylu. Nie zobaczysz tego w większości współczesnych skryptów Perla.

Kolejne popularne flagi, które możesz zobaczyć w linii hash-bang to -t i -T. Włączają one tak zwany taint-mode, który pomaga pisać bezpieczniejszy kod.