2008-12-13 56 views
22

Tôi có một tập lệnh Perl cần thực thi một tập lệnh Perl khác. Kịch bản thứ hai này có thể được thực hiện trực tiếp trên dòng lệnh, nhưng tôi cần thực thi nó từ bên trong chương trình đầu tiên của tôi. Tôi sẽ cần phải vượt qua nó một vài thông số mà thông thường sẽ được thông qua trong khi nó chạy độc lập (kịch bản đầu tiên chạy theo định kỳ, và thực thi kịch bản thứ hai theo một bộ điều kiện hệ thống nhất định).Làm cách nào để chạy tập lệnh Perl từ bên trong tập lệnh Perl?

Các tìm kiếm sơ bộ của Google đề xuất sử dụng dấu gạch chéo ngược hoặc cuộc gọi hệ thống(). Có cách nào khác để chạy nó không? (Tôi đoán có, vì đó là Perl chúng ta đang nói về: P) Phương pháp nào được ưa thích hơn nếu tôi cần lấy kết quả đầu ra từ chương trình được gọi (và, nếu có thể, đường ống xuất ra khi nó thực thi để stdout như thể thứ hai chương trình được gọi trực tiếp)?

(Edit: oh, now SO đề xuất một số câu hỏi liên quan. This one đóng, nhưng không chính xác giống như những gì tôi đang yêu cầu./O), vì vậy tôi không chắc chắn một lời kêu gọi một lần là phù hợp với điều này.)

Trả lời

28

Vị trí của thông dịch viên perl hiện tại của bạn có thể được tìm thấy trong biến đặc biệt $^X. Điều này quan trọng nếu perl không có trong đường dẫn của bạn, hoặc nếu bạn có nhiều phiên bản perl có sẵn nhưng để đảm bảo rằng bạn đang sử dụng cùng một phiên bản trên bảng.

Khi thực hiện các lệnh bên ngoài, bao gồm các chương trình Perl khác, xác định xem chúng có thực sự chạy có thể khá khó khăn không. Kiểm tra $? có thể để lại vết sẹo dài tinh thần, vì vậy tôi thích sử dụng IPC::System::Simple (có sẵn từ CPAN):

use strict; 
use warnings; 
use IPC::System::Simple qw(system capture); 

# Run a command, wait until it finishes, and make sure it works. 
# Output from this program goes directly to STDOUT, and it can take input 
# from your STDIN if required. 
system($^X, "yourscript.pl", @ARGS); 

# Run a command, wait until it finishes, and make sure it works. 
# The output of this command is captured into $results. 
my $results = capture($^X, "yourscript.pl", @ARGS); 

Trong cả hai ví dụ trên bất kỳ đối số bạn muốn vượt qua chương trình bên ngoài của bạn đi vào @ARGS. Vỏ cũng được tránh trong cả hai ví dụ trên, mang lại cho bạn lợi thế tốc độ nhỏ và tránh mọi tương tác không mong muốn liên quan đến các ký tự meta shell. Đoạn mã trên cũng mong đợi chương trình thứ hai của bạn trả về một giá trị thoát bằng 0 để biểu thị thành công; nếu đó không phải là trường hợp, bạn có thể chỉ định một cuộc tranh cãi đầu tiên bổ sung giá trị xuất cảnh cho phép:

# Both of these commands allow an exit value of 0, 1 or 2 to be considered 
# a successful execution of the command. 

system([0,1,2], $^X, "yourscript.pl", @ARGS); 
# OR 
capture([0,1,2, $^X, "yourscript.pl", @ARGS); 

Nếu bạn có một quá trình lâu dài và bạn muốn xử lý dữ liệu của nó khi nó được tạo ra, sau đó bạn' có lẽ sẽ cần một đường ống mở, hoặc một trong những mô-đun IPC nặng hơn từ CPAN. Đã nói tất cả điều đó, bất cứ khi nào bạn cần gọi một chương trình Perl khác từ Perl, bạn có thể cân nhắc xem việc sử dụng mô-đun có phải là lựa chọn tốt hơn hay không. Bắt đầu một chương trình khác mang lại một vài chi phí, cả về chi phí khởi động và chi phí I/O cho việc di chuyển dữ liệu giữa các quá trình. Nó cũng làm tăng đáng kể độ khó xử lý lỗi. Nếu bạn có thể biến chương trình bên ngoài của bạn thành một mô-đun, bạn có thể thấy nó đơn giản hóa thiết kế tổng thể của bạn.

All the best,

Paul

6

Sử dụng các dấu gạch chéo nếu bạn cần nắm bắt đầu ra của lệnh.

Sử dụng system nếu bạn không cần chụp đầu ra của lệnh.

TMTOWTDI: vì vậy cũng có nhiều cách khác, nhưng đó là hai cách dễ nhất và có nhiều khả năng nhất.

1
#!/usr/bin/perl 
use strict; 

open(OUTPUT, "date|") or die "Failed to create process: $!\n"; 

while (<OUTPUT>) 
{ 
    print; 
} 

close(OUTPUT); 

print "Process exited with value " . ($? >> 8) . "\n"; 

Điều này sẽ bắt đầu quá trình date và đưa đầu ra của lệnh vào tệp OUTPUT mà bạn có thể xử lý một dòng tại một thời điểm. Khi lệnh kết thúc, bạn có thể đóng tập tin đầu ra và lấy giá trị trả về của tiến trình. Thay thế date bằng bất cứ thứ gì bạn muốn.

3

Nếu bạn cần phải đồng bộ gọi kịch bản bên ngoài của bạn -bạn chỉ muốn khởi động nó và không chờ cho nó để finish-, sau đó:

# On Unix systems, either of these will execute and just carry-on 
# You can't collect output that way 
`myscript.pl &`; 
system ('myscript.pl &');  

# On Windows systems the equivalent would be 
`start myscript.pl`; 
system ('start myscript.pl'); 

# If you just want to execute another script and terminate the current one 
exec ('myscript.pl'); 
9

tôi có thể nghĩ ra một số cách để làm điều này. Bạn đã đề cập đến hai cái đầu tiên, vì vậy tôi sẽ không đi sâu vào chúng.

  1. backticks: $ retVal = `` perl somePerlScript.pl ;
  2. hệ thống() gọi
  3. eval

Các eval có thể được thực hiện bằng cách slurping tập tin khác vào một chuỗi (hoặc một danh sách các chuỗi), sau đó 'eval'ing các dây. Heres một mẫu:

#!/usr/bin/perl 
open PERLFILE, "<somePerlScript.pl"; 
undef $/; # this allows me to slurp the file, ignoring newlines 
my $program = <PERLFILE>; 
eval $program; 

4. làm:

do 'somePerlScript.pl'

+0

Sẽ dễ dàng hơn và đơn giản hơn để chỉ sử dụng 'do'? – innaM

+0

Cảm ơn. Tôi đã thêm 'do' vào danh sách. – Colin

4

Xem tài liệu perlipc cho một số tùy chọn để liên lạc giữa các quá trình.

Nếu tập lệnh đầu tiên của bạn chỉ thiết lập môi trường cho tập lệnh thứ hai, bạn có thể đang tìm kiếm exec.

9

Bạn đã có câu trả lời hay cho câu hỏi của mình, nhưng luôn có khả năng để có một quan điểm khác: có thể bạn nên xem xét sắp xếp lại tập lệnh mà bạn muốn chạy từ tập lệnh đầu tiên.Biến chức năng thành một mô-đun. Sử dụng mô-đun từ đầu tiên và từ tập lệnh thứ hai.

+0

Tôi đã xem xét cách đây một thời gian, nhưng đã viết nó vì hai lý do. Đầu tiên, một nhà phát triển khác đang làm việc trên chương trình thứ hai, và thứ hai, chương trình thứ hai được hoàn thành vài tháng trước, và kịch bản đầu tiên này để tự động hóa nó chỉ là ý nghĩ. Nếu tôi đã làm điều này một lần nữa, tôi có lẽ sẽ làm mô-đun thay thế. – cbowns

23

Bạn chỉ có thể làm nó.

{ 
    local @ARGV = qw<param1 param2 param3>; 
    do '/home/buddy/myscript.pl'; 
} 

Ngăn tải phí trên một bản sao perl khác.

0

Tôi muốn thực hiện một việc như thế này để giảm tải không phải chương trình con vào tệp bên ngoài để giúp chỉnh sửa dễ dàng hơn. Tôi thực sự làm điều này thành một chương trình con. Ưu điểm của cách này là các biến "của tôi" trong tệp ngoài được khai báo trong không gian tên chính. Nếu bạn sử dụng 'do', họ dường như không di chuyển sang không gian tên chính. Lưu ý rằng bản trình bày bên dưới không bao gồm xử lý lỗi

sub getcode($) { 
    my @list; 
    my $filename = shift; 
    open (INFILE, "< $filename"); 
    @list = <INFILE>; 
    close (INFILE); 
    return \@list; 
} 

# and to use it: 

my $codelist = []; 
$codelist = getcode('sourcefile.pl'); 
eval join ("", @$codelist); 
Các vấn đề liên quan