В чем проблема
Во-первых, как и для многих утилит, у вас возникнет проблема с именами файлов, начинающимися с -
. Пока в:
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
perl
v5.21.5 и выше имеют новый <<>>
оператор, который ведет себя так, <>
за исключением того, что он не будет выполнять эту специальную обработку. Аргументы будут рассматриваться только как имена файлов. Итак, с этими версиями вы можете написать:
perl -e 'while(<<>>){ ...;}' -- *
(не забывайте --
и не используйте ./*
), не опасаясь перезаписи файлов или выполнения неожиданных команд.
-n
/ -p
все еще используй опасную <>
форму. И остерегайтесь символических ссылок, которые все еще используются, так что это не обязательно означает, что их можно безопасно использовать в ненадежных каталогах.