Как передать содержимое переменной как STDIN в операторе qx{} в Perl?
Я в основном хотел бы сделать это:
$_ = "some content that need to be escaped &>|\"$\'`\s\\";
qx{echo $_ | foo}
Здесь есть две проблемы. Сначала содержание $_
необходимо экранировать, так как он может содержать двоичные данные. Во-вторых, ссылаясь echo
может быть немного неэффективным.
Как я могу просто передать некоторый контент как STDIN команде в Perl?
3 ответа
Этот ответ очень наивный подход. Это склонно к тупику. Не используйте это!
Икегами объясняет в комментарии ниже:
Если родитель записывает достаточно в канал, подключенный к STDIN дочернего элемента, и если дочерний процесс выводит достаточно в канал, подключенный к его STDOUT, до того, как он прочитает из своего STDIN, будет тупик. (В некоторых системах это может быть всего 4 КБ.) Решение заключается в использовании чего-то вроде select, потоков и т. Д. Лучшее решение - использовать инструмент, который уже решил проблему для вас (IPC::Run3 или IPC:: Бежать). IPC::Open2 и IPC::Open3 слишком низкоуровневые, чтобы быть полезными в большинстве случаев
Я оставлю оригинальный ответ, но рекомендую читателям выбрать решение из одного из других ответов.
Ты можешь использовать open2
из IPC:: Open2 для чтения и записи в один и тот же процесс.
Теперь вам не нужно заботиться о побеге.
use IPC::Open2;
use FileHandle;
my $writer = FileHandle->new;
my $reader = FileHandle->new;
my $pid = open2( $reader, $writer, 'wc -c' );
# write to the pipe
print $writer 'some content that need to be escaped &>|\"$\'`\s\\';
# tell it you're done
$writer->close;
# read the out of the pipe
my $line = <$reader>;
print $line;
Это напечатает 48
,
Обратите внимание, что вы не можете использовать двойные кавычки ""
для точного ввода вы показали, потому что количество обратных слешей \
неправильно.
Смотрите perldocopen
и perlipc для получения дополнительной информации.
Следующие предположим @cmd
содержит программу и ее аргументы (если есть).
my @cmd = ('foo');
Если вы хотите захватить вывод, вы можете использовать любое из следующего:
use String::ShellQuote qw( shell_quote );
my $cmd1 = shell_quote('printf', '%s', $_);
my $cmd2 = shell_quote(@cmd);
my $output = qx{$cmd1 | $cmd2};
use IPC::Run3 qw( run3 );
run3(\@cmd, \$_, \my $output);
use IPC::Run qw( run );
run(\@cmd, \$_, \my $output);
Если вы не хотите захватывать вывод, вы можете использовать любое из следующего:
use String::ShellQuote qw( shell_quote );
my $cmd1 = shell_quote('printf', '%s', $_);
my $cmd2 = shell_quote(@cmd);
system("$cmd1 | $cmd2");
system('/bin/sh', '-c', 'printf "%s" "$0" | "$@"', $_, @cmd);
use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote(@cmd);
open(my $pipe, '|-', $cmd);
print($pipe $_);
close($pipe);
open(my $pipe, '|-', '/bin/sh', '-c', '"$@"', 'dummy', @cmd);
print($pipe $_);
close($pipe);
use IPC::Run3 qw( run3 );
run3(\@cmd, \$_);
use IPC::Run qw( run );
run(\@cmd, \$_);
Если вы не хотите захватывать вывод, но не хотите его видеть, вы можете использовать любое из следующего:
use String::ShellQuote qw( shell_quote );
my $cmd1 = shell_quote('printf', '%s', $_);
my $cmd2 = shell_quote(@cmd);
system("$cmd1 | $cmd2 >/dev/null");
system('/bin/sh', '-c', 'printf "%s" "$0" | "$@" >/dev/null', $_, @cmd);
use String::ShellQuote qw( shell_quote );
my $cmd = shell_quote(@cmd);
open(my $pipe, '|-', "$cmd >/dev/null");
print($pipe $_);
close($pipe);
open(my $pipe, '|-', '/bin/sh', '-c', '"$@" >/dev/null', 'dummy', @cmd);
print($pipe $_);
close($pipe);
use IPC::Run3 qw( run3 );
run3(\@cmd, \$_, \undef);
use IPC::Run qw( run );
run(\@cmd, \$_, \undef);
Заметки:
Решения, использующие
printf
будет накладывать ограничение на размер данных для передачи в STDIN программы.Решения, использующие
printf
не могут передать NUL в STDIN программы.Представленные решения, использующие IPC::Run3 и IPC:: Run, не содержат оболочки. Это позволяет избежать проблем.
Вы, вероятно, должны использовать
system
а такжеcapture
из IPC::System::Simple вместо встроенногоsystem
а такжеqx
получить "бесплатную" проверку ошибок.
Мне нравится решение, предоставляемое @simbabque, поскольку оно избегает вызова Shell. Во всяком случае, для сравнения, более короткое решение может быть получено с помощью Bash (но избегая echo
) с помощью Bash Here string
:
$_ = q{some content that need to be escaped &>|\"$\'`\s\\};
$_ =~ s/'/'"'"'/g; # Bash needs single quotes to be escaped
system 'bash', '-c', "foo <<< '$_'";
И, если вам нужно захватить вывод команды:
use Capture::Tiny 'capture_stdout';
my $res = capture_stdout { system 'bash', '-c', "foo <<< '$_'" };