Có vấn đề gì vậy
Đầu tiên, giống như nhiều tiện ích, bạn sẽ gặp vấn đề với tên tệp bắt đầu bằng -
. Trong khi ở:
sh -c 'inline sh script here' other args
Các đối số khác được truyền cho inline sh script
; với perl
tương đương,
perl -e 'inline perl script here' other args
Các đối số khác được quét để có thêm tùy chọn cho perl trước, không phải cho tập lệnh nội tuyến. Vì vậy, ví dụ, nếu có một tệp được gọi -eBEGIN{do something evil}
trong thư mục hiện tại,
perl -ne 'inline perl script here;' *
(có hoặc không có -n
) sẽ làm điều gì đó xấu xa.
Giống như các tiện ích khác, công việc xung quanh đó là sử dụng điểm đánh dấu kết thúc tùy chọn ( --
):
perl -ne 'inline perl script here;' -- *
Nhưng ngay cả sau đó, nó vẫn nguy hiểm và điều đó thuộc về <>
nhà điều hành được sử dụng bởi -n
/ -p
.
Vấn đề được giải thích trong perldoc perlop
tài liệu.
Toán tử đặc biệt đó được sử dụng để đọc một dòng (một bản ghi, bản ghi là các dòng theo mặc định) của đầu vào, trong đó đầu vào đó đến từ mỗi đối số lần lượt được đưa vào @ARGV
.
Trong:
perl -pe '' a b
-p
ngụ ý một while (<>)
vòng lặp xung quanh mã (ở đây trống).
<>
đầu tiên sẽ mở a
, đọc các bản ghi một dòng tại một thời điểm cho đến khi tệp hết và sau đó mở b
...
Vấn đề là, để mở tệp, nó sử dụng hình thức đầu tiên, không an toàn open
:
open ARGV, "the file as provided"
Với hình thức đó, nếu đối số là
"> afile"
, nó mở afile
trong chế độ viết,
"cmd|"
, nó chạy cmd
và đọc đầu ra của nó.
"|cmd"
, bạn có một luồng mở để ghi vào đầu vào của cmd
.
Ví dụ:
perl -pe '' 'uname|'
Không xuất nội dung của tệp được gọi uname|
(tên tệp btw hoàn toàn hợp lệ), nhưng đầu ra của uname
lệnh.
Nếu bạn đang chạy:
perl -ne 'something' -- *
Và ai đó đã tạo một tệp có tên rm -rf "$HOME"|
(lại là tên tệp hoàn toàn hợp lệ) trong thư mục hiện tại (ví dụ vì thư mục đó đã từng được người khác ghi lại hoặc bạn đã trích xuất một tệp lưu trữ tinh ranh hoặc bạn đã chạy một số lệnh tinh ranh, hoặc một lỗ hổng khác trong một số phần mềm khác đã bị khai thác), sau đó bạn gặp rắc rối lớn. Các lĩnh vực quan trọng cần biết về vấn đề đó là các công cụ xử lý tệp tự động trong các khu vực công cộng như /tmp
(hoặc các công cụ có thể được gọi bằng các công cụ đó).
File gọi > foo
, foo|
, |foo
là một vấn đề. Nhưng ở mức độ thấp hơn < foo
và foo
với các ký tự khoảng cách ASCII hàng đầu hoặc dấu (bao gồm khoảng trắng, tab, dòng mới, cr ...) cũng như điều đó có nghĩa là các tệp đó sẽ không được xử lý hoặc sai.
Cũng lưu ý rằng một số ký tự trong một số bộ ký tự nhiều byte (như ǖ
trong BIG5-HKSCS) kết thúc bằng byte 0x7c, mã hóa của |
.
$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000 88 7c
210 |
0000002
Vì vậy, trong các địa phương sử dụng bộ ký tự đó,
perl -pe '' ./nǖ
Sẽ cố chạy ./n\x88
lệnh vì perl
sẽ không cố diễn giải tên tệp đó trong miền địa phương của người dùng!
Cách khắc phục / khắc phục
AFAIK, bạn không thể làm gì để thay đổi hành vi mặc định không an toàn đó perl
một lần và cho tất cả các hệ thống.
Đầu tiên, sự cố chỉ xảy ra với các ký tự ở đầu và cuối tên tệp. Vì vậy, trong khi perl -ne '' *
hoặc perl -ne '' *.txt
là một vấn đề,
perl -ne 'some code' ./*.txt
không bởi vì tất cả các đối số bây giờ bắt đầu với ./
và kết thúc ở .txt
(vì vậy không -
, <
, >
, |
, không gian ...). Tổng quát hơn, đó là một ý tưởng tốt để tiền tố globs với ./
. Điều đó cũng tránh các vấn đề với các tệp được gọi -
hoặc bắt đầu với -
nhiều tiện ích khác (và ở đây, điều đó có nghĩa là bạn không cần --
đánh dấu kết thúc tùy chọn ( ) nữa).
Sử dụng -T
để bật taint
chế độ giúp một phần nào đó. Nó sẽ hủy bỏ lệnh nếu gặp phải tệp độc hại đó (chỉ dành cho các trường hợp >
và |
trường hợp, chứ không phải <
hoặc khoảng trắng).
Điều đó hữu ích khi sử dụng các lệnh như vậy một cách tương tác để cảnh báo bạn rằng có điều gì đó tinh ranh đang diễn ra. Điều đó có thể không được mong muốn khi thực hiện một số xử lý tự động, vì điều đó có nghĩa là ai đó có thể khiến việc xử lý đó thất bại chỉ bằng cách tạo một tệp.
Nếu bạn muốn để xử lý tất cả các tập tin, bất kể tên của họ, bạn có thể sử dụng các ARGV::readonly
perl
mô-đun trên CPAN (không may thường không được cài đặt theo mặc định). Đó là một mô-đun rất ngắn mà:
sub import{
# Tom Christiansen in Message-ID: <24692.1217339882@chthon>
# reccomends essentially the following:
for (@ARGV){
s/^(\s+)/.\/$1/; # leading whitespace preserved
s/^/< /; # force open for input
$_.=qq/\0/; # trailing whitespace preserved & pipes forbidden
};
};
Về cơ bản, nó vệ sinh @ARGV bằng cách biến " foo|"
ví dụ thành "< ./ foo|\0"
.
Bạn có thể làm tương tự trong một BEGIN
tuyên bố trong perl -n/-p
lệnh của bạn :
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
Ở đây chúng tôi đơn giản hóa nó trên giả định ./
đang được sử dụng.
Một tác dụng phụ của điều đó (và ARGV::readonly
) mặc dù là $ARGV
trong your code here
đó cho thấy nhân vật NUL kéo dài.
Cập nhật 2015-06-03
perl
v5.21.5 trở lên có một <<>>
toán tử mới hoạt động giống như <>
ngoại trừ việc nó sẽ không thực hiện quá trình xử lý đặc biệt đó. Đối số sẽ chỉ được coi là tên tệp. Vì vậy, với các phiên bản đó, bây giờ bạn có thể viết:
perl -e 'while(<<>>){ ...;}' -- *
(đừng quên --
hoặc sử dụng ./*
mặc dù) mà không sợ nó ghi đè lên các tệp hoặc chạy các lệnh không mong muốn.
-n
/ -p
vẫn sử dụng <>
hình thức nguy hiểm mặc dù. Và các liên kết cẩn thận vẫn đang được theo dõi, vì vậy điều đó không nhất thiết có nghĩa là nó an toàn để sử dụng trong các thư mục không đáng tin cậy.