2012-06-20 37 views
6

Nguyên nhân gốc rễ của câu hỏi này là nỗ lực của tôi để viết các bài kiểm tra cho một mô-đun xử lý tùy chọn/đối số mới (OptArgs) cho Perl. Điều này tất nhiên liên quan đến phân tích cú pháp @ARGV mà tôi đang làm dựa trên các câu trả lời cho câu hỏi this. Điều này hoạt động tốt trên các hệ thống mà I18N :: Langinfo :: CODESET được định nghĩa [1].Trong Perl, làm cách nào để chuyển đối số unicode sang các lệnh bên ngoài?

Trên hệ thống nơi langinfo(CODESET) không có sẵn, tôi muốn ít nhất là cố gắng hết sức dựa trên hành vi được quan sát. Tuy nhiên, các thử nghiệm của tôi cho đến nay cho thấy rằng một số hệ thống tôi thậm chí không thể vượt qua đối số unicode cho tập lệnh ngoài đúng cách.

Tôi đã quản lý để chạy một cái gì đó như sau trên các hệ thống khác nhau ở đâu "test_script" là một kịch bản Perl mà chỉ đơn thuần là thực hiện một print Dumper(@ARGV):

use utf8; 
my $utf8 = '¥'; 
my $result = qx/$^X test_script $utf8/; 

Những gì tôi đã tìm thấy là trên FreeBSD các test_script nhận byte mà có thể được giải mã thành định dạng nội bộ của Perl. Tuy nhiên trên OpenBSD và Solaris test_script xuất hiện để có được chuỗi "\x{fffd}\x{fffd}" chỉ chứa ký tự thay thế unicode (hai lần?).

Tôi không biết cơ chế nằm trong toán tử qx. Tôi đoán nó hoặc là exec 's hoặc vỏ ra, nhưng không giống như filehandles (nơi tôi có thể binmode chúng cho mã hóa) Tôi không biết làm thế nào để đảm bảo nó làm những gì tôi muốn. Tương tự với system() cho vấn đề đó. Vì vậy, câu hỏi của tôi là những gì tôi không làm đúng ở trên? Nếu không những gì là khác nhau với Perl hoặc vỏ hoặc môi trường trên OpenBSD và Solaris?

[1] Thực ra tôi nghĩ cho đến nay chỉ có Linux theo kết quả kiểm tra CPAN.

Cập nhật (x2): Tôi hiện có sau chạy theo cách của mình thông qua các thiết lập cpantester để kiểm tra giả thuyết Schwern của:

use strict; 
use warnings; 
use Data::Dumper; 

BEGIN { 
    if (@ARGV) { 
     require Test::More; 
     Test::More::diag("\npre utf8::all: " 
       . Dumper({ utf8 => $ARGV[0], bytes => $ARGV[1] })); 
    } 
} 

use utf8; 
use utf8::all; 

BEGIN { 
    if (@ARGV) { 
     Test::More::diag("\npost utf8::all: " 
       . Dumper({ utf8 => $ARGV[0], bytes => $ARGV[1] })); 
     exit; 
    } 
} 

use Encode; 
use Test::More; 

my $builder = Test::More->builder; 
binmode $builder->output,   ':encoding(UTF-8)'; 
binmode $builder->failure_output, ':encoding(UTF-8)'; 
binmode $builder->todo_output, ':encoding(UTF-8)'; 

my $utf8 = '¥'; 
my $bytes = encode_utf8($utf8); 

diag("\nPassing: " . Dumper({ utf8 => $utf8, bytes => $bytes, })); 

open(my $fh, '-|', $^X, $0, $utf8, $bytes) || die "open: $!"; 
my $result = join('', <$fh>); 
close $fh; 

ok(1); 
done_testing(); 

tôi sẽ đăng kết quả trên các hệ thống khác nhau khi họ đi qua. Bất kỳ ý kiến ​​về tính hợp lệ và tính chính xác của điều này sẽ được đánh giá cao. Lưu ý rằng đó là không phải được dự định là thử nghiệm hợp lệ. Mục đích của việc trên là để có thể so sánh những gì được nhận trên các hệ thống khác nhau.

Độ phân giải: Vấn đề cơ bản thực sự hóa ra là một thứ không được giải quyết trong câu hỏi của tôi cũng như câu trả lời của Schwern bên dưới. Những gì tôi phát hiện ra là một số máy cpantesters chỉ có một locale ascii được cài đặt/có sẵn. Tôi không nên mong đợi bất kỳ nỗ lực nào để chuyển các ký tự UTF-8 cho các chương trình trong loại môi trường này để hoạt động. Vì vậy, cuối cùng vấn đề của tôi là điều kiện thử nghiệm không hợp lệ, không phải mã không hợp lệ.

Tôi chưa thấy gì từ trước đến nay để chỉ ra rằng toán tử qx hoặc mô-đun utf8::all có bất kỳ ảnh hưởng nào đến cách thông số được chuyển đến các chương trình bên ngoài. Thành phần quan trọng dường như là các biến môi trường LANG và/hoặc LC_ALL môi trường, để thông báo cho chương trình bên ngoài những gì miền địa phương mà họ đang chạy.

Nhân tiện, xác nhận ban đầu của tôi là mã của tôi đang hoạt động trên tất cả các hệ thống mà I18N :: Langinfo :: CODESET được định nghĩa là không chính xác.

+0

Trên ghi chú có liên quan, các BSD dường như bị hỏng theo các cách khác. Tôi thậm chí không thể gõ ký tự unicode thông qua một phiên ssh để FreeBSD - mà kết quả trong hành vi thiết bị đầu cuối lẻ. –

+0

unicode-via-ssh có thể phụ thuộc rất nhiều vào thiết bị đầu cuối bạn đang sử dụng và những gì 'TERM' của bạn là trên cả hai hệ thống. – sarnold

+0

Tôi không thể sao chép vấn đề của bạn trên OS X, nhưng bạn có thể muốn thử [utf8 :: all] (https://metacpan.org/module/utf8::all) để bật hầu hết các tính năng Unicode bao gồm Unicode '@ ARGV'. 'qx' cũng có thể bị ảnh hưởng bởi' pragma 'mở, mà' utf8 :: all' sử dụng để làm cho filehandles tôn trọng Unicode. – Schwern

Trả lời

1

qx thực hiện cuộc gọi đến trình bao và nó có thể gây cản trở.

Để tránh điều đó, hãy sử dụng utf8::all để bật tất cả các vần quảng cáo Perl Unicode. Sau đó, sử dụng chức năng open để mở một đường ống đến chương trình của bạn, tránh vỏ.

use utf8::all; 
my $utf8 = '¥'; 

open my $read_from_script, "-|", "test_script", $utf8; 
print <$read_from_script>,"\n"; 
+0

Tránh sử dụng trình bao với phiên bản mở 3 đối số là một gợi ý tốt. Tuy nhiên tôi không thể thấy những gì có hiệu lực utf8 :: tất cả là nghĩa vụ phải có trên các đối số cho các chức năng 'mở' cũng không phải để gọi' exec' cơ bản. –

+0

Nhìn vào nguồn của utf8 :: tất cả nó thực sự làm cho các giả định về mã hóa của '@ ARGV' mà [this] (http://stackoverflow.com/questions/2037467/how-can-i-treat-command-line -arguments-as-utf-8-in-perl) cảnh báo chống lại việc làm. Tuy nhiên đó là nhận ra chủ đề từ câu hỏi này. –

+0

@MarkLawrence 'utf8 :: all' đang có hiệu ứng thông qua 'pragma' mở. Cụ thể 'sử dụng mở": std "' xuất hiện để có hiệu lực ống mở ra, có lẽ bằng cách làm cho STDOUT sử dụng UTF-8. Một ví dụ tốt của nó là "để người khác tìm ra và sử dụng mô-đun của họ". Và vâng, nó đang đưa ra một giả định về việc mã hóa '@ ARGV'. Bạn phải thực hiện một giả định, ngay cả khi bạn không giả sử ASCII, và UTF-8 là một cược khá an toàn. Thật không may nó không phải là một trong đó có thể được thực hiện lexically. – Schwern

Các vấn đề liên quan