Как я могу получить всю строку командной строки? - PullRequest
3 голосов
/ 26 июня 2010

Я пишу скрипт на Perl, имитирующий gcc. Этот мой скрипт должен обработать стандартный вывод из gcc. Часть для обработки закончена, но я не могу заставить работать простую часть: как я могу переслать все параметры командной строки, как есть, следующему процессу (в моем случае gcc). Командные строки, отправляемые в gcc, имеют тенденцию быть очень длинными и потенциально могут содержать множество escape-последовательностей, и я не хочу сейчас играть в эту игру с экранированием, и я знаю, что в сложных случаях это сложно сделать правильно на окнах.

В основном, gcc.pl немного crazies\ t\\ "command line\"", и этот gcc.pl должен перенаправить эту же командную строку в настоящий gcc.exe (я использую windows).

Я делаю это так: open("gcc.exe $cmdline 2>&1 |"), так что stderr из gcc подается в stdout, а мой скрипт на perl обрабатывает stdout. Проблема в том, что я нигде не могу найти, как построить это $cmdline.

Ответы [ 4 ]

2 голосов
/ 26 июня 2010

«Safe Pipe Opens» в документации perlipc описывает, как получить выходные данные другой команды, не беспокоясь о том, как оболочка ее проанализирует. Этот метод обычно используется для безопасной обработки ненадежных входных данных, но он также избавляет вас от подверженной ошибкам задачи правильного экранирования всех аргументов.

Поскольку он обходит оболочку, вам нужно создать эффект 2>&1 самостоятельно, но, как вы увидите ниже, это легко сделать.

#! /usr/bin/perl

use warnings;
use strict;

my $pid = open my $fromgcc, "-|";
die "$0: fork: $!" unless defined $pid;

if ($pid) {
  while (<$fromgcc>) {
    print "got: $_";
  }
}
else {
  # 2>&1
  open STDERR, ">&STDOUT" or warn "$0: dup STDERR: $!";

  no warnings "exec";  # so we can write our own message
  exec "gcc", @ARGV       or die  "$0: exec: $!";
}

Сама Windows не поддерживает open FH, "-|", но Cygwin радостно делает это:

$ ./gcc.pl foo.c
got: gcc: foo.c: No such file or directory
got: gcc: no input files
2 голосов
/ 26 июня 2010

Я бы использовал AnyEvent :: Subprocess :

use AnyEvent::Subprocess;

my $process_line = sub { say "got line: $_[0]" };

my $gcc = AnyEvent::Subprocess->new(
    code      => ['gcc.exe', @ARGV],
    delegates => [ 'CompletionCondvar', 'StandardHandles', {
         MonitorHandle => {
              handle   => 'stdout',
              callback => $process_line,
         }}, {
         MonitorHandle => {
              handle   => 'stderr',
              callback => $process_line,
         }},
    ],
);

my $running = $gcc->run;
my $done = $running->recv;
$done->is_success or die "OH NOES";

say "it worked";

Делегат MonitorHandle работает как перенаправление, за исключением того, что у вас есть возможность использовать отдельный фильтр для каждого из stdout и stderr. Аргумент «code» - это arrayref, представляющий команду для запуска.

1 голос
/ 26 июня 2010

Ознакомьтесь с функцией exec и функцией system в Perl.

Если вы предоставите любой из них массив аргументов (а не одну строку), он напрямую вызовет функцию Unix execve () или близкого родственника, не позволяя оболочке интерпретировать что-либо так, как вам нужно. .

0 голосов
/ 26 июня 2010

Спасибо за ответы, я пришел к выводу, что допустил большую ошибку: снова коснулся Perl: потрачены часы времени, чтобы выяснить, что это не может быть сделано должным образом.Perl использует другой способ разделения параметров командной строки, чем все другие приложения, использующие MS stdlib (что является стандартным для win32).Из-за этого некоторые параметры командной строки, которые должны были интерпретироваться как аргумент командной строки signle, perl может интерпретироваться как более чем один аргумент.Это означает, что все, что я пытаюсь сделать, это пустая трата времени из-за такого некорректного поведения в perl.Невозможно выполнить эту задачу правильно, если я 1) не могу получить доступ к исходной командной строке как есть и 2) perl не разделяет аргументы командной строки правильно.как простой тест:

script.pl """test |test"

на win32 будет неправильно интерпретировать командную строку как:

ARGV=['"test', '|test']

Принимая во внимание, что правильный «ответ» на окнах должен быть

ARGV=['"test |test']

Я использовал Perl ActiveState, я пробовал также последнюю версию клубничного Perl: оба отстой.Похоже, что Perl, который поставляется с msys, работает должным образом, скорее всего потому, что он был построен против mingw вместо времени выполнения cygwin? ..

Проблема и причина в perl состоит в том, что у него глючный синтаксический анализатор строк cmd, и он выиграл 'не работает на окнах Неважно, что Cygwin поддерживает или нет.У меня есть простой случай, когда переменная окружения (которую я не могу контролировать) расширяется до

perl gcc.pl -c  "-IC:\ffmpeg\lib_avutil\" rest of args

Perl видит, что у меня есть только два аргумента: -c и '-IC: \ ffmpeg \ lib_avutil "Остальные аргументы«тогда как любая соответствующая реализация Windows получает вторую строку команды cmd в виде: '-IC: \ ffmpeg \ lib_avutil \', это означает, что perl - огромная куча мусора для моего простого случая, потому что она не предоставляет адекватных средств для доступа к cmdСтроковые аргументы. Мне лучше использовать boost :: regex и выполнять весь мой синтаксический анализ непосредственно в c ++, по крайней мере я никогда не буду делать глупых ошибок, таких как ne и! = для сравнения строк и т. д. Правила экранирования Windows для аргументов командной строкидовольно странно, но они стандартны для Windows, и Perl по какой-то странной причине не хочет следовать правилам ОС.

...