В то время как более поздние версии Windows имеют where
команда, можно также сделать это с Windows XP при помощи модификаторов переменной среды, следующим образом:
c:\> for %i in (cmd.exe) do @echo. %~$PATH:i
C:\WINDOWS\system32\cmd.exe
c:\> for %i in (python.exe) do @echo. %~$PATH:i
C:\Python25\python.exe
Вам не нужны никакие дополнительные инструменты, и это не ограничено PATH
, так как можно заменить любой переменной среды (в формате пути, конечно), что Вы хотите использовать.
И, если Вы хотите тот, который может обработать все расширения в PATHEXT (поскольку сам Windows делает), этот добивается цели:
@echo off
setlocal enableextensions enabledelayedexpansion
:: Needs an argument.
if "x%1"=="x" (
echo Usage: which ^<progName^>
goto :end
)
:: First try the unadorned filenmame.
set fullspec=
call :find_it %1
:: Then try all adorned filenames in order.
set mypathext=!pathext!
:loop1
:: Stop if found or out of extensions.
if "x!mypathext!"=="x" goto :loop1end
:: Get the next extension and try it.
for /f "delims=;" %%j in ("!mypathext!") do set myext=%%j
call :find_it %1!myext!
:: Remove the extension (not overly efficient but it works).
:loop2
if not "x!myext!"=="x" (
set myext=!myext:~1!
set mypathext=!mypathext:~1!
goto :loop2
)
if not "x!mypathext!"=="x" set mypathext=!mypathext:~1!
goto :loop1
:loop1end
:end
endlocal
goto :eof
:: Function to find and print a file in the path.
:find_it
for %%i in (%1) do set fullspec=%%~$PATH:i
if not "x!fullspec!"=="x" @echo. !fullspec!
goto :eof
Это на самом деле возвращает все возможности, но можно настроить его довольно легко для определенных поисковых правил.
Во-первых, проверка равенства строк в Perl - это eq
, а не ==
.
Если у вас есть методы для выполнения работы, скажем, с именем bit и ftp,
my %proc = (
bit => \&bit,
ftp => \&ftp,
);
my $proc = $proc{$trans_type};
$proc->() if defined $proc;
Вы можете использовать для этого хеш ...
Пусть каждый метод передачи регистрирует себя в хеше. $ MyApp :: TransferManager :: МЕТОДЫ {ftp} = \ & do_ftp; sub do_ftp {...} 1;
Каждый метод передачи использует согласованный API. Может быть, это просто функция, или это может быть интерфейс объекта.
Вызов передачи через хэш.
sub do_transfer {
# ...
мой $ sub = $ MyApp :: TransferManager :: МЕТОДЫ {$ метод}
или квакать "Неизвестный метод передачи $ method";
$ sub -> ($ arg1, $ arg2, ...);
# ...
}
Кстати: метод регистрации OO будет выглядеть примерно так:
package MyApp::TransferManager;
use Carp;
use strict;
my %registered_method;
sub register {
my ($class, $method, $sub) = @_;
exists $registered_method{$method}
and croak "method $method already registered";
$registered_method{$method} = $sub;
}
# ...
1;
(Ни один из этих кодов не протестирован; прошу прощения за пропущенные точки с запятой)
Правильная конструкция здесь - завод. Посмотрите, как с этим справляется DBI
. В итоге вы получите класс TransferAgent
, который создает один из любого количества классов TransferAgent :: *
. Очевидно, вам понадобится больше проверок ошибок, чем предоставляет реализация ниже. Использование подобной фабрики означает, что вы можете добавлять новые типы агентов передачи без необходимости добавлять или изменять какой-либо код.
TransferAgent.pm - фабричный класс:
package TransferAgent;
use strict;
use warnings;
sub connect {
my ($class, %args) = @_;
require "$class/$args{type}.pm";
my $ta = "${class}::$args{type}"->new(%args);
return $ta->connect;
}
1;
TransferAgent / Base.pm
- содержит базу функциональность класса TransferAgent :: *
:
package TransferAgent::Base;
use strict;
use warnings;
use Carp;
sub new {
my ($class, %self) = @_;
$self{_files_transferred} = [];
$self{_bytes_transferred} = 0;
return bless \%self, $class;
}
sub files_sent {
return wantarray ? @{$_[0]->{_files_sent}} :
scalar @{$_[0]->{_files_sent}};
}
sub files_received {
return wantarray ? @{$_[0]->{_files_recv}} :
scalar @{$_[0]->{_files_recv}};
}
sub cwd { return $_[0]->{_cwd} }
sub status { return $_[0]->{_connected} }
sub _subname {
return +(split "::", (caller 1)[3])[-1];
}
sub connect { croak _subname, " is not implemented by ", ref $_[0] }
sub disconnect { croak _subname, " is not implemented by ", ref $_[0] }
sub chdir { croak _subname, " is not implemented by ", ref $_[0] }
sub mode { croak _subname, " is not implemented by ", ref $_[0] }
sub put { croak _subname, " is not implemented by ", ref $_[0] }
sub get { croak _subname, " is not implemented by ", ref $_[0] }
sub list { croak _subname, " is not implemented by ", ref $_[0] }
1;
TransferAgent / FTP.pm
- реализует (фиктивный) FTP-клиент:
package TransferAgent::FTP;
use strict;
use warnings;
use Carp;
use base "TransferAgent::Base";
our %modes = map { $_ => 1 } qw/ascii binary ebcdic/;
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
$self->{_mode} = "ascii";
return $self;
}
sub connect {
my $self = shift;
#pretend to connect
$self->{_connected} = 1;
return $self;
}
sub disconnect {
my $self = shift;
#pretend to disconnect
$self->{_connected} = 0;
return $self;
}
sub chdir {
my $self = shift;
#pretend to chdir
$self->{_cwd} = shift;
return $self;
}
sub mode {
my ($self, $mode) = @_;
if (defined $mode) {
croak "'$mode' is not a valid mode"
unless exists $modes{$mode};
#pretend to change mode
$self->{_mode} = $mode;
return $self;
}
#return current mode
return $self->{_mode};
}
sub put {
my ($self, $file) = @_;
#pretend to put file
push @{$self->{_files_sent}}, $file;
return $self;
}
sub get {
my ($self, $file) = @_;
#pretend to get file
push @{$self->{_files_recv}}, $file;
return $self;
}
sub list {
my $self = shift;
#pretend to list remote files
return qw/foo bar baz quux/;
}
1;
script.pl
- как использовать TransferAgent:
#!/usr/bin/perl
use strict;
use warnings;
use TransferAgent;
my $ta = TransferAgent->connect(
type => "FTP",
host => "foo",
user => "bar",
password => "baz",
);
print "files to get: ", join(", ", $ta->list), "\n";
for my $file ($ta->list) {
$ta->get($file);
}
print "files gotten: ", join(", ", $ta->files_received), "\n";
$ta->disconnect;
I have several examples in Mastering Perl in the sections on dynamic subroutines.
OO было бы излишним. Мое решение, вероятно, будет выглядеть примерно так:
sub ftp_transfer { ... }
sub bit_transfer { ... }
my $transfer_sub = { 'ftp' => \&ftp_transfer, 'bit' => \&bit_transfer, ... };
...
sub upload_file {
my ($file, ...) = @_;
...
$transfer_sub->{$file->{trans_type}}->(...);
}
Вы сказали, что сначала он будет использовать FTP, а позже перейдет к другим методам передачи. Я бы не стал называть «элегантным», пока вам действительно не понадобится добавить вторую или третью технологию. Этот второй метод передачи может никогда не потребоваться. : -)
Если вы хотите сделать это как «научный проект», тогда отлично.
Я устал видеть объектно-ориентированные шаблоны проектирования, усложняющие решения проблем, которые никогда не возникают.
Оберните первый метод передачи в метод uploadFile. Добавьте if, то else для второго метода. Получите элегантность и проведите рефакторинг третьего метода. К тому времени у вас будет достаточно примеров, так что ваше решение, вероятно, будет довольно общим.
Конечно, я хочу сказать, что второй и третий методы могут никогда не потребоваться.