ДМИТРИЙ ГОРЯИНОВ
FAQ Perl
По материалам www.xpoint.ru
Чем отличается синтаксис скриптов на UNIX- и WIN-платформах?
Юниксовые скрипты плохо воспринимает досовский перевод строки – CR LF. Если открыть такой файл в vi в конце строк будут ^M. Удалить их можно, например таким скриптом:
#!/bin/bash
install -d -m 0775 orig
cp $1 orig/$1.orig.“date +%m-%d-%H.%M“
sed -e "s/^M//g" $1 >oooo
mv -f oooo $1
Как мне получить зашифрованный пароль?
Стандартной функцией crypt(), например вот так:
# Calculate salt value for crypt function
@saltair= split //,"ABCDEFGHIJKLMNOPQRSTUVWXYZ abcedfghijklmnopqrstuvwxyz0123456789.";
sub get_salt {
srand();
$iOff = int(rand($#saltair));
$iOff2 = int(rand($#saltair));
return join("",$saltair[$iOff],
$saltair[$iOff2]);
}
$crypted_passwd = crypt($plain_passwd,get_salt());
А как мне проверить соответствие введенного пароля зашифрованному?
Первых два символа пароля соджержат шифровальный ключ. Если, взяв их, зашифровать проверяемый пароль, то зашифрованные строки должны совпасть. Пример:
if (crypt($entered_passwd,
subst($crypted_passwd,0,2))
eq $crypted_passwd) {
# Пароль верен
} else {
# Пароль не верен
}
Как сделать так, чтобы скрипт работал в фоновом режиме, как демон?
Варианта два. Первый – воспользоваться модулем Proc::Daemon, второй – сделать все самому, примерно так:
use strict;
require "sys/syscall.ph";
# Устанавливаем путь по умолчанию
$ENV{PATH} = "/bin:/usr/bin";
# Чисто для прикола
$0="mydaemon";
# Отделяемся от родителя
fork() && exit;
# Отключаемся от терминала
close STDOUT; close STDERR; close STDIN;
# Делаем корень текушим каталогом
chdir "/";
# Создаем новую сессию и становимся лидером группы процессов, чтоб нас случайно не прибили
syscall(&SYS_setsid);
# Перехватываем сигналы, для корректного выхода
$SIG{"INT"} = $SIG{"QUIT"} = $SIG{"TERM"} = "quit";
$SIG{"HUP"} = "ignore";
# Делаем наши темные дела
...
# Выходим
quit();
sub quit {
# Помещаем сюда код для корректного прекращения работы
...
exit(0);
}
Если вы хотите написать демона, реализующего работу через сеть, рекомендуем ознакомиться с модулем Net::Daemon.
Как защитить мою программу, чтобы никто не смог её прочитать?
Perl-сценарий представляет собой открыто распространяемый код.
Как сделать так, чтобы программа гарантированно работала только в одном экземпляре?
Способ первый, принятый в мире UNIX:
$pidfile = "/var/run/mydaemon.pid";
if (-e $pidfile) {
# aha, pid file is here, but process could be dead
by now
my $myfile=file_name($0);
unless (open(PIDFILE,$pidfile)) {
# too dangerous to start because I can’t read old
PID
exit 1;
}
my $oldpid=;
close PIDFILE;
# see if there is a process with such pid
if ($oldpid > 1 && kill(0,$oldpid)) {
# another proccess is running already
exit 1;
} else {
# that process is long dead
}
}
# write pid file
open (PID, ">$pidfile") or die;
print PID $$;
close(PID);
# do some work
...
# remove pid file
unlink $pidfile;
exit(0);
Способ второй, основанный на блокировании файлов:
# make a lock
$lockfilename="/tmp/mydaemon.lock";
unless (open (LOCKFILE,">$lockfilename")) {
die "cannot open lock file ";
}
unless (flock (LOCKFILE,LOCK_EX|LOCK_NB)){
print "my copy is already running ";
exit(0);
}
# do some work
...
# unlock lock file
close(LOCKFILE);
unlink($lockfilename);
Можно ли скомпилировать из Perl исполняемый файл?
Вы можете воспользоваться программой Perl2Exe. Это утилита для преобразования Perl-сценариев в выполняемые файлы, не требующие присутствия интерпретатора языка Perl.
Perl2Exe может сгенерировать модули для Win32 и многих клонов UNIX.
Perl2Exe также позволяет вам создавать не консольные программы, с использованием Tk.
http://www.indigostar.com/perl2exe.htm – разработчик IndigoSTAR Software.
Еще один продукт IndigoSTAR Software – SendMail for Windows(TM) – Windows версия популярной программы Unix Sendmail. Она позволяет отправлять сообщений из командной строки, CGI сценария или BAT-файла.
Proc::Background – Общий ли для UNIX и Win32 интерфейс управление фоновыми процессами?
Это общий интерфейс для управления фоновыми процессами как на UNIX, так и на Win32 платформах. Модуль позволяет вам запускать и завершать фоновые процессы, получать выходные данные и отслеживать состояние фоновых процессов.
P.S. Рекомендую при использовании под Win32 брать архив со CPAN и посмотреть прилагаемые примеры и скрипты.
Proc::Background – http://search.cpan.org/search?dist=Proc-Background.
Как можно стандартизировать (оформить в виде процедуры) получение выборки из БД, чтобы получать набор записей с именованными полями?
Это можно сделать так:
sub QueryArrayOfHashes
my ($DB, $query) = @_;
my ($result,$data_hash,@items,$key,
$val,%hash);
$result = $DB->prepare($query);
$result->execute or return;
while ($data_hash=$result->fetchrow_hashref)
%hash=%$data_hash;
push @items,{%hash};
}
$result->finish;
@items;
}
Комментарии:
- $data_hash – ссылка на хэш;
- %$data_hash == %{$data_hash} – получение самого хэша из ссылки;
- {%hash} = разименованный хэш – чтоб получился массив хэшей, а не просто один массив;
- @items == return @items – в данном случае.
Пример использования:
use DBI;
...
$dbh=DBI->connect("DBI:mysql:mysql:localhost", $user, $password, {RaiseError => 1})
or die "connecting : $DBI::errstr ";
@res = QueryArrayOfHashes($dbh, "select user, password from user");
for ($i=0; $i<=$#res; $i++) {
print " [Record #$i]:: ";
foreach $key (sort keys %{$res[$i]}) {
# запись вида $a[1]{b} эквивалентна $a[1]->{b}
print $key, " ", $res[$i]{$key}, " ";
}
$dbh->disconnect;
Как удалить дерево каталогов?
В «Perl Cookbook» by Tom Christiansen and Nathan Torkington, O’Reilly («Библиотека программиста: Perl», издательство «Питер»), приводится два примера рекурсивного удаления каталога вместе с его содержимым. В одном используется функция finddepth из модуля File::Find, во втором - функция rmtree из File::Path.
Вот еще один способ:
# в качестве параметров скрипт принимает список директорий для удаления
die "usage: $0 [ ... ] " unless @ARGV;
foreach $path (@ARGV) {
del_folder($path);
}
sub del_folder {
my $dir=shift;
return 0 unless $dir;
my (@dirs,@files,$filename,
$newdir,$list);
opendir(DIR,$dir) or (warn "Can’t rmdir $dir: $!" and return 0);
@dirs=grep {!(/^./) && -d "$dir/$_"} readdir(DIR);
rewinddir(DIR);
@files=grep {!(/^.(.)?$/) && -f "$dir/$_"}
readdir(DIR);
closedir (DIR);
for $list(0..$#dirs) {
$newdir=$dir."/".$dirs[$list];
del_folder($newdir);
}
for $list(0..$#files) {
$filename=$dir."/".$files[$list];
unlink $filename or (warn "Can’t unlink $filename: $!" and next);
}
rmdir $dir or (warn "Can’t rmdir $dir: $!" and return 0);
return 1;
}