Как передать содержимое переменной как 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 <<< '$_'" };
Другие вопросы по тегам