В чем проблема
Во-первых, как и для многих утилит, у вас возникнет проблема с именами файлов, начинающимися с -. Пока в:
sh -c 'inline sh script here' other args
Другие аргументы передаются в inline sh script; с perlэквивалентом,
perl -e 'inline perl script here' other args
Другие аргументы проверяются на наличие дополнительных опций для perl , а не для встроенного скрипта. Так, например, если -eBEGIN{do something evil}в текущем каталоге есть файл ,
perl -ne 'inline perl script here;' *
(с или без -n) будет делать что-то злое.
Как и для других утилит, обходной путь для этого заключается в использовании маркера конца опций ( --):
perl -ne 'inline perl script here;' -- *
Но даже тогда это все еще опасно, и это зависит от <>оператора, используемого -n/ -p.
Проблема объяснена в perldoc perlopдокументации.
Этот специальный оператор используется для чтения одной строки (одна запись, по умолчанию записи являются строками) ввода, причем этот ввод поступает из каждого переданного аргумента @ARGV.
В:
perl -pe '' a b
-pподразумевает while (<>)цикл вокруг кода (здесь пустой).
<>будет сначала открывать a, читать записи по одной строке, пока файл не исчерпан, а затем открыть b...
Проблема в том, что для открытия файла используется первая небезопасная форма open:
open ARGV, "the file as provided"
С этой формой, если аргумент
"> afile", он открывается afileв режиме записи,
"cmd|", он работает cmdи читает его вывод.
"|cmd"Вы открыли поток для записи на вход cmd.
Так, например:
perl -pe '' 'uname|'
Не выводит содержимое названного файла uname|(кстати, совершенно правильное имя файла), но вывод unameкоманды.
Если вы бежите:
perl -ne 'something' -- *
И кто-то создал файл с именем rm -rf "$HOME"|(опять же совершенно правильное имя файла) в текущем каталоге (например, потому что этот каталог был доступен для записи для других пользователей, или вы извлекли хитрый архив, или вы выполнили какую-то хитрую команду, или была использована другая уязвимость в другом программном обеспечении), тогда у вас большие проблемы. Области, где важно знать об этой проблеме, - это инструменты, автоматически обрабатывающие файлы в общественных местах /tmp(или инструменты, которые могут вызываться такими инструментами).
Файлы называются > foo, foo|, |fooявляются проблемой. Но в меньшей степени < fooи fooс начальными или конечными пробелами ASCII (включая пробел, табуляцию, символ новой строки, cr ...), а также это означает, что эти файлы не будут обработаны или будут неправильными.
Также помните, что некоторые символы в некоторых многобайтовых наборах символов (как ǖв BIG5-HKSCS) заканчиваются байтом 0x7c, кодировкой |.
$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000 88 7c
210 |
0000002
Так что в регионах, использующих эту кодировку,
perl -pe '' ./nǖ
Попробовал бы запустить ./n\x88команду , как perlбы не пытаться интерпретировать это имя файла локали пользователя!
Как исправить / обойти
AFAIK, вы ничего не можете сделать, чтобы изменить это небезопасное поведение по умолчанию perlодин раз и для всей системы.
Во-первых, проблема возникает только с символами в начале и конце имени файла. Итак, пока perl -ne '' *или perl -ne '' *.txtесть проблема,
perl -ne 'some code' ./*.txt
не потому , что все аргументы теперь начинаются с ./и заканчиваются .txt(так не -, <, >, |, космос ...). В целом, это хорошая идея , чтобы Приставка шарики с ./. Это также позволяет избежать проблем с файлами, вызываемыми -или начинающимися со -многих других утилит (и здесь, это означает, что вам больше не нужен --маркер end-of-options ( )).
Использование -Tдля включения taintрежима помогает в некоторой степени. Он прервет команду , если такой вредоносный файл встречается (только для >и |случаях, не <или пробелы , хотя).
Это полезно при использовании таких команд в интерактивном режиме, поскольку это предупреждает вас о том, что происходит что-то хитрое. Это может быть нежелательно при выполнении какой-либо автоматической обработки, поскольку это означает, что кто-то может сделать эту обработку неудачной, просто создав файл.
Если вы хотите обработать каждый файл, независимо от их имени, вы можете использовать в ARGV::readonly perlмодуль на CPAN ( к сожалению , как правило , не устанавливается по умолчанию). Это очень короткий модуль, который делает:
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
};
};
По сути, он очищает @ARGV, превращаясь, " foo|"например, в "< ./ foo|\0".
Вы можете сделать то же самое в BEGINинструкции в вашей perl -n/-pкоманде:
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
Здесь мы упрощаем это в предположении, ./что используется.
Побочным эффектом этого (и ARGV::readonly), однако, является то, что $ARGVв your code hereпоказывает, что завершающий символ NUL.
Обновление 2015-06-03
perlv5.21.5 и выше имеют новый <<>>оператор, который ведет себя так, <>за исключением того, что он не будет выполнять эту специальную обработку. Аргументы будут рассматриваться только как имена файлов. Итак, с этими версиями вы можете написать:
perl -e 'while(<<>>){ ...;}' -- *
(не забывайте --и не используйте ./*), не опасаясь перезаписи файлов или выполнения неожиданных команд.
-n/ -pвсе еще используй опасную <>форму. И остерегайтесь символических ссылок, которые все еще используются, так что это не обязательно означает, что их можно безопасно использовать в ненадежных каталогах.