【问题标题】:How can I get entire command line string?如何获取整个命令行字符串?
【发布时间】:2011-03-08 13:32:47
【问题描述】:

我正在编写一个模仿 gcc 的 perl 脚本。这是我的脚本需要处理来自 gcc 的一些标准输出。处理部分已经完成,但我无法让简单的部分工作:如何将所有命令行参数按原样转发到下一个进程(在我的情况下为 gcc)。发送到 gcc 的命令行往往很长,并且可能包含大量转义序列,我现在不想玩转义的游戏,而且我知道在复杂情况下在 Windows 上正确使用它很棘手。

基本上, gcc.pl 一些 crazies\ t\\ "command line\"" 并且 gcc.pl 必须将相同的命令行转发到真正的 gcc.exe(我使用 windows)。

我这样做:open("gcc.exe $cmdline 2>&1 |") 以便将来自 gcc 的 stderr 馈送到 stdout,我的 perl 脚本处理 stdout。问题是我在任何地方都找不到如何构造$cmdline

【问题讨论】:

    标签: perl command-line command-line-arguments


    【解决方案1】:

    阅读 Perl 中的 exec 函数和 system 函数。

    如果您为其中任何一个提供参数数组(而不是单个字符串),它会直接调用 Unix execve() 函数或近亲,而不让 shell 解释任何内容,完全按照您的需要执行.

    【讨论】:

    • 这很接近,但我最初选择使用 open 是因为我实际上需要 shell 的一些帮助:我将 stderr 转发到 stdout,然后将 stdout 提供给我的脚本。系统(如果它与 C 中的系统相同)不允许我处理标准输出(除非我对标准文件描述符做了一些黑魔法)并且 exec 也不允许我这样做(显然)。另外,我需要逐行处理(我用 g++/gcc 编译的程序非常复杂,需要几秒钟来编译每个文件,我需要逐行处理而不是冻结几秒钟,然后转储所有标准输出。跨度>
    • 那么,在这一点上,我使用 open 的决定是否正确,或者我需要查看 exec/system 调用?似乎引用规则在 Windows 上并没有那么复杂,似乎我应该自己手动编写(例如 php 有简单的 api 用于 shell 引用,这对我的情况很有效)。
    • open 像 exec 和 system 一样接受一个列表,除非你的 perl 非常老。
    【解决方案2】:

    我会使用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 委托的工作方式类似于重定向,但您可以选择为每个标准输出和标准错误使用单独的过滤器。 “code”arg 是一个 arrayref,表示要运行的命令。

    【讨论】:

    • 这似乎是我需要的;)谢谢!我知道很多工具/sdk 都使用这种技术,但我只是没有他们中的任何一个进行这种转发,看看他们是如何在 perl 中实现它的。我将stderr转发到stdout,这样我就不需要单独处理它们(如果我做得不好,可能会死锁)。似乎 AnyEvent::Subprocess 对我来说是正确的解决方案。
    • 其实,我只是尝试在 Windows 上安装 AE::Subprocess,但事情并不正常。叹。使用 cygwin :)
    • 行不通,我运行 VisualStudio 构建过程中的所有垃圾,所以 cygwin 不是一个选项。我试图在 AE::Subprocess 内部查看他们可能用于引用的特定于 win32 的代码,但什么也没看到。
    【解决方案3】:

    感谢您的回答,我得出结论,我犯了一个大错误,我再次接触了 perl:浪费了数小时的时间来发现它无法正确完成。 Perl 使用与使用 MS stdlib(在 win32 上是标准的)的所有其他应用程序不同的方式来拆分命令行参数。

    由于某些命令行参数本应被解释为一个单一的命令行参数,因此 perl 可以将其解释为多个参数。这意味着我要做的所有事情都是浪费时间,因为 perl 中的错误行为。如果我 1) 无法按原样访问原始命令行并且 2) perl 没有正确拆分命令行参数,则无法正确完成此任务。

    作为一个简单的测试:

    script.pl """test |test"
    

    在 win32 上会错误地将命令行解释为:

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

    然而,Windows 上的正确“答案”必须是

    ARGV=['"test |test']
    

    我使用了 activestate perl,我还尝试了最新版本的草莓 perl:两者都很糟糕。似乎 msys 附带的 perl 可以正常工作,很可能是因为它是针对 mingw 而不是 cygwin 运行时构建的?..

    perl 的问题和原因在于它有错误的 cmd 行解析器,并且无论 cygwin 支持与否,它都无法在 Windows 上工作。 我有一个简单的情况,环境变量(我无法控制)扩展到

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

    Perl 发现我只有两个 args:-c 和 '-IC:\ffmpeg\lib_avutil" rest of args' 而任何符合要求的 Windows 实现都会接收第二个 cmd 行 arg 为:'-IC:\ffmpeg\lib_avutil\',这意味着对于我的简单案例来说 perl 是一大堆垃圾,因为它没有提供足够的方法来访问 cmd 行论据。我最好使用 boost::regex 并直接在 c++ 中进行所有解析,至少我不会犯像 ne 和 != 这样的愚蠢错误来比较字符串等。Windows 的命令行参数转义规则很奇怪,但它们在 Windows 上是标准的,而 perl 出于某种奇怪的原因不想遵循操作系统的规则。

    【讨论】:

    • 你不公正地将cmd.exe 的缺点归咎于 perl。命令 shell 拆分参数,而不是 perl。如果你将@ARGV 直接插入到另一个命令中(也就是说,运行 another 传递的 shell 参数解析),你会得到不同的结果。
    • 你错了。命令 shell 不会拆分 args。在 Windows 上,进程将命令行作为字符串获取,libc 将参数拆分。在 windows 上构建的所有 perl 的问题可能是因为它们使用不同的算法(这在 windows 世界中是错误的)来拆分 args(提示:他们需要使用 msvcrt lib 或编写符合 windows 的变体)。
    【解决方案4】:

    "Safe Pipe Opens" 在 perlipc 文档中描述了如何获取另一个命令的输出,而不必担心 shell 将如何解析它。该技术通常用于安全处理不受信任的输入,但它也可以避免您正确转义所有参数的容易出错的任务。

    因为它避开了外壳,所以您需要自己创建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 正常 does not support open FH, "-|",但 Cygwin 很高兴:

    $ ./gcc.pl foo.c
    得到:gcc:foo.c:没有这样的文件或目录
    得到:gcc:没有输入文件

    【讨论】:

    • 像这样将 STDERR 复制到 STDOUT,您将获得两全其美
    猜你喜欢
    • 2018-09-28
    • 2019-03-21
    • 1970-01-01
    • 2014-07-17
    • 1970-01-01
    • 1970-01-01
    • 2016-10-06
    • 1970-01-01
    相关资源
    最近更新 更多