Uruchamianie

Perl FAQ | RecentChanges | Preferences

1. Dlaczego nie mogę uruchomić skryptu?
2. Co oznacza dany błąd?
2.1. Text file busy
2.2. Premature end of script headers
2.3. Malformed header from script
2.4. Wypisywanie znaków nie od razu lub nie w kolejności
2.5. Can't locate
2.6. Can't load (locate loadable) object
2.7. Can't find string terminator
2.8. Might be a runaway multi-line "" string
2.9. Can't call method xxx on an undefined value
2.10. Name xxx used only once: possible typo
2.11. Global symbol xxx requires explicit package name
3. Jak uruchomić z argumentami/parametrami?
4. Jak uruchomić inny program ze skryptu?
5. Jak uruchomić z prawami roota?
6. Jak uruchomić na/co określony czas?
7. W czym pisać skrypty?
8. Jak testować skrypty?
9. Czy jest perl dla DOS/Windows?
10. Czy jest bezpłatny serwer z Perlem?
11. Czy mam moduł XX?
12. Jak zainstalować moduł?
13. Co znajduje się w danym module?
14. Jak zostawić działający skrypt (w tle)?
15. Jak pokazać, że skrypt działa (wiatraczek)?
16. A co to są wątki?
17. Czy skrypt może co sekundę coś robić?
18. Czy jest proces nr X?
19. Jak dostać listę procesów?
20. Jak obsługiwać kilka zadań równolegle?
21. Co to jest kompilator perla?
22. Jak ukryć kod programu?

1. Dlaczego nie mogę uruchomić skryptu?

   perl -pi -e 's/\r\n/\n/' skrypt
   perl -p -i.bak -e "s/\r//" plik   [wersja dla DOSa]
   perl -e "print 'Hello World'"
   perl -e "print \"Hello World\""

więcej informacji o skryptach przez WWW w dziale Internet.

2. Co oznacza dany błąd?

2.1. Text file busy

Plik był używany przez inny program, lub próba podmiany skryptu w trakcie jego działania.

2.2. Premature end of script headers

Twój skrypt nie generuje właściwych nagłówków HTTP - zobacz w dziale WWW.

2.3. Malformed header from script

Nagłówek HTTP co prawda jest, ale zły - zobacz w dziale WWW.

2.4. Wypisywanie znaków nie od razu lub nie w kolejności

Brakuje tzw. autoflush'a. Dobrze jest na początku dodać:

 $|=1;

2.5. Can't locate

Cały komunikat wygląda np. tak:

 Can't locate strict.pm in @INC (@INC contains: ...)

Oznacza to, że moduł (strict.pm) nie jest zainstalowany w miejscu gdzie powinien, tj. w ścieżkach wymienionych w @INC. Może to najczęściej oznaczać brak modułu (konieczność instalacji), lub konieczność wskazania ścieżki:

 use lib 'sciezka';

2.6. Can't load (locate loadable) object

Obiekt (moduł), który chcemy załadować istnieje, lecz zawiera błędy. Najczęściej nie ma na końcu 1; (jeśli pisaliśmy sami) lub został źle zainstalowany (np. przekopiowany, podczas gdy wymagana była instalacja).

2.7. Can't find string terminator

Nie znaleziono zakończenia tekstu. Należy zwrócić uwagę czy tekst zakończenia (np. EOM, _EOF_) jest jedynym w tej linijce i po nim znajduje znak nowej linii oraz nie ma innych znaków jak np. spacja.

2.8. Might be a runaway multi-line "" string

Najprawdopodobniej nie został poprawnie zamknięty cudzysłów wskazujący ciąg znaków.

2.9. Can't call method xxx on an undefined value

W podanej linii miała zostać wykonana funkcja o nazwie xxx, na obiekcie o podanej wartości. Nie została ona wykonana, gdyż błędem zakończyła się wcześniejsza funkcja zapisująca tę wartość.

2.10. Name xxx used only once: possible typo

Tu cię perl ostrzega (opcja -w), że tylko raz używasz danej zmiennej. Sprawdź, czy nie zrobiłeś literówki w nazwie.

2.11. Global symbol xxx requires explicit package name

Przed pierwszym użyciem danej zmiennej trzeba dać my lub our, w zależności od zasięgu zmiennej.

3. Jak uruchomić z argumentami/parametrami?

Chodzi o uruchomienie typu:

 ./test.pl -a1 -b2

W skrypcie dostęp do argumentów jest poprzez wektor @ARGV, czyli w tym przypadku w $ARGV[0] będzie wartość "-a1". Do używania z duchem Unixa, tj. tak by wartość powyższego a była 1, służy poniższy schemat:

 use Getopt::Std;
 getopts("a:b:");
 print "${opt_a}\n";
 print "${opt_b}\n";

4. Jak uruchomić inny program ze skryptu?

Do uruchamiania służy polecenie system (patrz niżej) lub użycie składni `komenda`. Dobrze jest też używać pełnych ścieżek do komend np. trzy sposoby wywołania komendy finger root:

 @linie_odpowiedzi = `/usr/bin/finger root`

 $status_komendy = system ("/usr/bin/finger root");

 open(IN,"/usr/bin/finger root |");
 @linie_odpowiedzi=<IN>;
 close IN; 

Nie należy stosować exec, bo nie zwraca, tzn. po wywołaniu tej funkcji nowy proces (program) zastępuje ten aktualnie wykonywany i następne polecenia po exec nie będą wykonane.

Jeśli chcemy wywołać skrypt poleceniem system i zobaczyć tekst:

 use IPC::Run qw(run);
 my @cmd = qw(echo -n to ma iść na stdout);
 run \@cmd, \'', \my $out, \my $err or die "run <@cmd>: $!";
 print "polecenie <@cmd> zwróciło '$out' na STDOUT i '$err' na STDERR\n";

Powyższy kod jest najbezpieczniejszy. Inny skrypt perla można też uruchomić:

 $wynik = do "nazwaskryptu.cgi";

Do dwustronnej komunikacji z zewnętrznym programem, jeśli mamy możliwość jego modyfikacji służą funkcje IPC (man perlipc). Jeśli potrzeba udawać użytkownika w przykładzie z finger należy zamiast open użyć open2 (IPC::Open2) lub open3 (IPC::Open). Alternatywą jest moduł Expect.

5. Jak uruchomić z prawami roota?

Opis problemu bezpieczeństwa znajduje się w manie perlsec. Schemat skryptu:

 $ENV{'PATH'} = '/bin:/usr/bin';
 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};  #bezpieczne środowisko

 system('id');             # sprawdzenie kto wykonuje skrypt
 $<=$>;                    # ustaw rzeczywiste id na efektywne id
 $(=$)+0;                  # ustaw rzeczywiste gid na efektywne gid
 system('id');             # sprawdzenie praw

działanie polega na uruchomieniu perla z odpowiednimi prawami najlepiej przez sudo (http://www.courtesan.com/sudo/), a w starszych wersjach z suidperl.

6. Jak uruchomić na/co określony czas?

 {
  local $SIG{ALRM} = sub { die "TIMEOUT\n" };
  alarm( 10 ); # maksymalny czas operacji w sekundach
  eval {
    # tutaj operacje które mogą trwać zbyt długo - np. łączenie się
    # do odległego serwera
    alarm(0); # wyłączamy timeout
  };
  if( $@ eq "TIMEOUT\n" ) {
     # był timeout
  }
  elsif( $@ ) {
     # był jakiś inny fatal error
  }
  else {
     # było OK
  }
 }

To działa tak, że w przypadku gdy operacje wewnątrz eval{ } przekroczą zadany czas, system generuje sygnał ALRM, który z kolei powoduje wykonanie die i "wyskoczenie" poza eval{ }, z ustawieniem $@ na "TIMEOUT\n".

To rozwiązanie nie jest w 100% bezpieczne, bo obsługa sygnałów w perlu (ciągle) nie jest w 100% bezpieczna. Ale przy mało krytycznych zastosowaniach sprawdza się doskonale.

W środowisku Windows nie ma funkcji alarm, więc trzeba się posłużyć modułami serii Win32.

Do uruchamiania co jakiś czas lepszym rozwiązaniem jest zastosowanie np. crona.

7. W czym pisać skrypty?

Najlepszym edytorem jest ulepszony vi o nazwie VIM. Posiada np. podświetlanie składni. Jest też makro robiące z kodu perla, hateemela pokolorowanego zgodnie z bieżącymi ustawieniami. Aby go włączyć należy wpisać :syntax on

Do funcji debug przyda się http://iijo.org/vimDebug/

Inne polecane edytory:

Dobre środowiska IDE:

8. Jak testować skrypty?

Przydatne przy testowaniu skryptów jest używanie opcji -w w jednym z dwóch wariantów (z linii komend lub w pierwszej linii skrytpu):

 perl -w skrypt.pl

 #!/usr/local/bin/perl -w 

Pomocne, choć dość uciążliwe jest stosowanie konstrukcji:

 use warnings;
 use strict;
 use diagnostics -verbose; 

Warto też zwrócić uwagę na sprawdzanie czy wywołanie funkcji powiodło się, czyli co ona zwróciła. Na przykład przy otwieraniu pliku można zastosować:

 open PLIK,"plik.txt" or die "Otwarcie pliku: $!";

Testowanie struktur danych w programie ułatwia moduł Data::Dumper, np.:

  use Data::Dumper; 
  print Dumper(\@a);

Uruchamiając skrypt z opcją perla -d (podobnie jak wyżej -w) mamy do dyspozycji wbudowany debugger.

Do testowania skryptów CGI polecane jest sprawdzenie, czy przy wykonywaniu z linii komend, nie ma błędów oraz czy wyświetlane są odpowiednie nagłówki. Przykładowe wywołanie skryptu CGI z parametrem nazwa o wartości nowa:

 ./skrypt.cgi nazwa=nowa

Testowanie CGI można także wykonywać na lokalnym serwerze WWW np. dla systemu Windows. Bardziej polecane jest użycie serwera Apache ze względu na rozpowszechnienie i zgodność.

Do kontroli błędów skryptu dobrze jest go uruchomić z linii komend lub wstawić procedurę obsługi błędów (sygnału DIE) przez WWW:

 $SIG{__DIE__} = sub {
 die @_ if ! defined $^S;                # blad w obsludze bledu
 print "Content-type: text/plain\n\n";   # naglowek dla WWW
 print "Blad: $_\n";                     # opis bledu
 }

Powyższe można także uzyskać stosując w skrypcie linię

 use CGI::Carp 'fatalsToBrowser';

W CPANie znajduje się moduł CGI::Debug, dzięki któremu możemy sprawdzić co jest nie tak w skrypcie umieszając linię:

 use CGI::Debug(report =>'everything', on=>'errors');

9. Czy jest perl dla DOS/Windows?

Tak. Więcej informacji jak zwykle w CPANie w dziale ports. Np.:

ftp://sunsite.icm.edu.pl/pub/CPAN/ports/index.html#win32

Dla WIN 32 to doradza się: ActivePerl for x86 ze strony http://www.activestate.com/ lub http://theoryx5.uwinnipeg.ca/ppms/

Mały perl (560 kB) jest pod adresem http://www.mhuffman.com/resource/perl/perlmin586.zip

Dla zwolenników mod_perla przyda się adres ftp://theoryx5.uwinnipeg.ca/pub/other pod którym znajduje się jego wersja dla tego systemu, a także moduły bazujące na mod_perlu.

10. Czy jest bezpłatny serwer z Perlem?

Ponieważ Perl jest bezpłatny na każdą platformę, więc możesz go zainstalować na swoim komputerze. Jeśli jednak chcesz mieć dostęp do Perla, to zobacz:

11. Czy mam moduł XX?

Moduły generalnie posiadają rozszerzenie .pm i są instalowane w jednym z katalogów zawartych w tablicy @INC (wkompilowanej w program perl). Zaimportowane moduły mieszczą się w hashu %INC. Lista standardowych:

 perldoc perlmodlib

Listę niestandardowych (dodatkowych) modułów zainstalowanych można uzyskać po wydaniu polecenia:

 perldoc perllocal

Komenda

 perl -Mnazwa_modulu -e 'print "OK\n"'

sprawdzi czy moduł da się załadować. Natomiast komenda szukająca plików o rozszerzeniu .pm w katalogach związanych z perlem (@INC) to:

 perl -e "foreach \$path (@INC) { system(\"find \$path -name '*.pm'\") }"

Ewentualnie można objerzeć wynik komendy

 perl -MCPAN -e autobundle

Lista standardowych modułów dla ActivePerla jest znacznie większa niż dla wersji dla Unixa. Lista tych pierwszych jest pod:

12. Jak zainstalować moduł?

Jeśli nie znamy nazwy lub nie możemy znaleźć modułu przydatny może okazać się adres http://search.cpan.org/ wyszukiwarki. Moduł CPAN też ma taką możliwość (patrz niżej). Niektóre moduły występują tylko w pakietach np.

LWP - libwwwperl
MIME::* - mimetools
Net::* - libnet

Instalacja standardowo po ściągnięciu z CPANu (np. GD):

 gzip -cd GD*.tar.gz |tar xvf -
 cd GD-cośtam # to co się zrobiło po rozpakowaniu
 perl Makefile.PL # jesli chcemy na swoim koncie: perl Makefile.PL LIB=~/lib
 make
 make test    # opcjonalnie
 make install # do tego trzeba odpowiednie prawa (zwykle root)

Szybciej:

 perl -MCPAN -e shell 
 cpan> i /GD/
 [wyniki]
 cpan> install GD

Przy pierwszym uruchomieniu moduł CPAN wymaga konfiguracji i zapisuje ją w pliku CPAN/Config.pm w swoim katalogu bibliotecznym. Dla instalacji niestandardowej można go skopiować jako $HOME/.cpan/CPAN/MyConfig.pm i poprawić.

Dla perla z Activestate, lista: http://aspn.activestate.com/ASPN/Modules instalacja (online):

 ppm
 PPM>install GD
 PPM>quit

offline:

Ściągnąć odpowiedniego zipa z
http://ppm.activestate.com/PPMPackages/zips/8xx-builds-only/Windows/ - 5.8
http://www.activestate.com/PPMPackages/5.6/zips/ - dla Perla 5.6
http://www.activestate.com/PPMPackages/5.005/zips/ - dla 5.005
lub http://www.activestate.com/ASPN/Downloads/ActivePerl/PPM/Zips

Rozpakować tylko tego zipa w odpowiednim katalogu, a potem
 ppm install GD.ppd

Dla tej wersji Perla istnieje sporo modułów tylko dla Windows z serii Win32::* opisanych w książce "Perl dla administratorów systemów".

Dla modułów zainstalowanych niestandardowo należy w skrypcie podać ścieżkę do nich (i jest ona pierwsza na liście) np.:

 use lib "$ENV{HOME}/lib";  
 use Nazwa::Modułu;

Pierwszą linię można zapisać też jako:

 BEGIN { unshift(@INC,"$ENV{HOME}/lib") }

lub zastąpić ustawieniem zmiennej PERLLIB lub PERL5LIB.

Do każdej instalacji z CPANu konieczny jest program make (standardowy w *nixie), dla windows używamy borlandowego dmake, lub microsoftowego nmake.

ftp://ftp.microsoft.com/Softlib/MSLFILES/nmake15.exe

Kolejność z nmake jest następująca:

Rozpakować plik o rozszerzeniu .tar.gz (np. WinZipem)
Uruchomić linię poleceń i wejść do katalogu
gdzie rozpakowano (np. cd c:\tmp)
 perl Makefile.PL 
 nmake
 nmake test
 nmake install

13. Co znajduje się w danym module?

Wszystkie nazwy w module są dostępne w hashu %nazwa_modulu::

 perl -MCPAN -e 'print join "\n", keys %CPAN::'

to dla modułu CPAN, zaś nazwy funkcji:

 perl -MCPAN -e 'print join "\n", grep *{$CPAN::{$_}}{CODE}, keys %CPAN::'

14. Jak zostawić działający skrypt (w tle)?

 use POSIX;
 #czesc dzialajaca normalnie
 fork() and exit();            # rozgalezienie procesu i zakonczenie glownego
 setsid();                     # nowa sesja
 # zamkniecie urzadzen we/wy
 close(STDIN);
 close(STDOUT);
 close(STDERR);
 #czesc wykonujaca prace w tle

Gotowy do wykorzystania moduł to Net::Server::Daemonize

Ponieważ na DOS/Win powyższy przykład nie działa, używa się modułu Win32::Process:

 use Win32;
 use Win32::Process; 

 $perlPath = 'C:/perl/bin/perl.exe'; # Sciezka do perla
 $skrypt = 'perl -w D:/ppm/test_dbf/import.pl'; # Wywolywany skrypt
 $workDir = 'D:/ppm/test_dbf'; #Katalog roboczy

 Win32::Process::Create($Win32::Process::Create::ProcessObj,
                       $perlPath,
                       $skrypt,
                       0,
                       DETACHED_PROCESS,
                       $workDir) or die print_error();

 sub print_error { return Win32::FormatMessage(Win32::GetLastError()); }

Do postawienia serwisu na NT przyda się moduł Win32::Deamon ze strony http://www.roth.net

15. Jak pokazać, że skrypt działa (wiatraczek)?

 $|++;
 @a=("|","/","-","\\"); 
 while(1) {
   print $a[0];
   push@a, shift @a; 
   sleep 1;
   print "\b";
 }

lub

 $|++;
 @a=("|","/","-","\\"); 
 while(1) {
   print "$a[$i++]\r";
   $i%=4;
   sleep 1;
 }

16. A co to są wątki?

perldoc perlthrtut

Wątki współdzielą pamięć. Czyli jak w jednym wątku zmienisz wartość zmiennej globalnej, to wszystkie inne wątki (z danego procesu) będą tą zmianę widzieć.

To oznacza dużo prostszą komunikację między wątkami (nie trzeba kombinować z pamięcią dzieloną/socketami/sygnałami), ale zarazem pojawiają się problemy z synchronizacją (wszystkie operacje na zmiennych globalnych trzeba zabezpieczać semaforami).

Poza tym wątki są dużo tańsze, tzn. utworzenie nowego wątku jest dużo mniej kosztowne niż utworzenie nowego procesu. No i oczywiście zajmują mniej zasobów niż procesy.

17. Czy skrypt może co sekundę coś robić?

Jeśli potrzeba, by skrypt co pewien czas dał znać o tym co robi, można wykorzystać do tego sygnały:

 $| = 1;  
 $SIG{ALRM} = sub { print '#'; alarm 1; };
 print "Zaczynam\n";
 alarm 1;

 for (1..10000000) { }; # cos dlugiego

 # skonczone, kasujemy alarm i obsluge
 alarm 0;
 delete $SIG{ALRM};
 print "Skonczylem\n";      

W środowisku Windows nie ma funkcji alarm, więc trzeba się posłużyć modułami serii Win32.

18. Czy jest proces nr X?

 use POSIX qw(:errno);

 if( ! kill( 0, $X ) && $! == ESRCH ) { print "procesu nie ma"; }
 else { print "proces jest"; }

19. Jak dostać listę procesów?

Jeśli chodzi o unixa, można użyć najszybszego rozwiązania:

 $wynik = `ps aux`;

Alternatywą jest moduł Proc::ProcessTable.

20. Jak obsługiwać kilka zadań równolegle?

Tworzymy np. 10 procesów potomnych (w tle), które wysyłają coś do rodzica i kończą działanie po losowym czasie. Rodzic odczytuje przez potok (pipe) i wypisuje na ekran.

 # do konwersji numer procesu -> deskryptor
 my %pid2fh;           
 no strict 'refs';

 for my $i ( 1..10 ) {
   #otwarcie potoku do czytania i rozdwojenie procesu
   my $pid = open( "F$i", "-|" );        
   if( $pid ) { $pid2fh{$pid} = "F$i"; } #zapis deskryptora jesli rodzic
   else {
     my $timeout = int rand 10;      # losowe opoznienie
     exec "sleep $timeout; echo 'Hello, I am $$'"; # zakonczenie napisem
   };
 }

 while( 1 ) {
   my $pid = wait;                   # czekaj na potomka
   last if $pid < 0;                 # jesli nie ma zadnego koniec petli
   # proces $pid zakończył działanie, czytamy co zrobił...
   my $fh = $pid2fh{$pid};
   while(<$fh>) { print "[$pid]: $_"; }
 }

open F, "-|" powoduje "rozforkowanie" procesu, w procesie potomnym wynikiem tej funkcji jest 0 (co wykorzystujemy powyżej aby uruchomić zadanie zewnętrzne przez system - exec byłby tu optymalniejszy), natomiast proces pierwotny otrzymuje jako wynik funkcji pid procesu potomnego. Co ważniejsze, tak stworzony proces ma od razu swoje standardowe wyjście podpięte do otwartego właśnie filehandle, więc możemy z niego czytać.

Funkcja wait zwraca pid procesu który się zakończył, lub -1 jeżeli nie ma już żadnych działających procesów potomnych (jeżeli są procesy potomne ale żaden się jeszcze nie skończył, funkcja wait zatrzymuje się aż do czasu zakonczenia jednego z procesów potomnych).

Do komunikacji dwustronnej można wykorzystać funkcję open2 oraz manual perlipc.

21. Co to jest kompilator perla?

Perl jest językiem interpretowanym, do jego użycia potrzebny jest zatem interpreter o nazwie perl. Istnieją jednak programy do zamiany formatu tekstowego skryptu na format wykonywalny - nazywane kompilatorami. Są one w przeciwieństwie do perla płatne. Są to:

 perlcc (freeware wraz z perlem)
 perlapp (licencja: 195$, tydzień free)
   http://www.activestate.com/Products/Perl_Dev_Kit/index.html
 perl2exe (licencja pro: 149$, lite: 49$) 
   http://www.indigostar.com/perl2exe.htm
 p2xstd.exe (???)

perlapp i perl2exe potrafią tworzyć biblioteki dll, przez co skrypt jest mniejszy. Czasem trzeba deklarować niepotrzebne normalnie biblioteki lub wydać polecenie:

 perl2exe_include IO/File.pm

lub umieścić w kodzie komentarz:

 use Archive::Zip; #perl2exe

Skrypt po skompilowaniu jest nieczytelny dla człowieka, nie potrzeba do jego wykonania interpretera, zajmuje przeciętnie około 1MB. Nie jest to jednak żadne zabezpieczenie przed próbami odczytania kodu źródłowego (np. moduł B::Deparse) http://www.perlmonks.com/index.pl?node_id=237943 http://www.perlmonks.com/index.pl?node_id=232983 Lepiej do tegu użyć pakietu PAR, Filter, ACME lub SHC.

Skompilowany skrypt jest przydatny gdy nie chcemy instalować perla.

Alternatywą polecaną zamiast perl2exe jest użycie pakietu PAR.

22. Jak ukryć kod programu?

Właściwie nie da się ukryć kodu na pewno. Pewnym rozwiązaniem jest jego zaciemnienie (np. przez skrypt wsadzenie wszystkiego w jedną linię, lub dziwne łamania linii, bez komentarzy, bez formatowania, z dużą liczbą zmiennych domyślnych). Lecz ciężko się wtedy poprawia i można to łatwo "odciemnić", np.:

 #!/usr/bin/perl -pi.bak
 BEGIN{ @ARGV or print "\nusage: ./wciecia.pl filename\n\n" and exit; }
 s/^\s*/"\t" x $count/e;
 $count += tr/{// - tr/}//;

Inne rozwiązanie to kompilacja, która jest procesem odwracalnym. Jedynie wstawienie skryptu przez WWW, tak że widoczne są tylko wyniki jego działania, zabezpiecza przed dostępem do kodu źródłowego.


Perl FAQ | RecentChanges | Preferences
This page is read-only | View other revisions
Last edited March 5, 2007 10:14 am by Juszkiew (diff)
Search:
Strona znajduje się na serwerze KT AGH.