我正在编写一个名为perl5i的perl模块。它的目标是在一个模块中修复一大堆常见的Perl问题(使用许多其他模块)。
要在命令行上调用它,你会写一个内容:perl -Mperl5i -e 'say "Hello"'
我认为这太冗长所以我想提供一个perl5i包装器,这样你就可以编写perl5i -e 'say "Hello"'
。我也希望人们能够用#!/usr/bin/perl5i
编写脚本,所以它必须是一个已编译的C程序。
我认为我所要做的就是将“-Mperl5i”推到参数列表的前面并调用perl。这就是我的尝试。
#include <unistd.h>
#include <stdlib.h>
/*
* Meant to mimic the shell command
* exec perl -Mperl5i "$@"
*
* This is a C program so it works in a #! line.
*/
int main (int argc, char* argv[]) {
int i;
/* This value is set by a program which generates this C file */
const char* perl_cmd = "/usr/local/perl/5.10.0/bin/perl";
char* perl_args[argc+1];
perl_args[0] = argv[0];
perl_args[1] = "-Mperl5i";
for( i = 1; i <= argc; i++ ) {
perl_args[i+1] = argv[i];
}
return execv( perl_cmd, perl_args );
}
Windows使这种方法复杂化。显然,Windows中的程序不传递参数数组,它们将所有参数作为单个字符串传递,然后进行自己的解析!因此perl5i -e "say 'Hello'"
之类的内容会变成perl -Mperl5i -e say 'Hello'
,Windows无法处理缺少引用的问题。
那么,我该如何处理呢?在Windows上包装引号中的所有内容并转义?有没有图书馆来处理这个问题?有更好的方法吗?我可以不在Windows上生成C程序并将其写为perl包装器,因为它不支持#!呢?
更新:更明确一点,这是随附的软件,因此需要使用某个shell或调整shell配置(例如,alias perl5i='perl -Mperl5i'
)的解决方案并不令人满意。
答案 0 :(得分:4)
对于Windows,请使用批处理文件。
perl5i.bat
@echo off
perl -Mperl5i %*
%*
是所有命令行参数减去%0
。
在Unixy系统上,类似的shell脚本就足够了。
<强>更新强>
我认为这会有效,但我不是shell向导,我没有便于测试的* nix系统。
perl5i
#!bash
perl -Mperl5i $@
再次更新:
DUH!现在我正确理解了您的#!
评论。我的shell脚本将在CLI中运行,但不在#!
行,因为#!foo
要求foo
是二进制文件。
忽略之前的更新。
似乎Windows使一切变得复杂。 我认为你最好使用批处理文件。
您可以use a file association,将.p5i
与perl -Mperl5i %*
相关联。当然这意味着在注册表中捣乱,最好避免IMO。最好包含有关如何在文档中手动添加关联的说明。
又一次更新
您可能希望了解parl如何做到这一点。
答案 1 :(得分:2)
我无法重现您描述的行为:
/* main.c */
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
for (i = 0; i < argc; i++) {
printf("%s\n", argv[i]);
}
return 0;
}
C:\> ShellCmd.exe a b c
ShellCmd.exe
a
b
c
使用Visual Studio 2005。
答案 2 :(得分:0)
Windows总是奇怪的情况。就个人而言,我不会尝试代码为Windows环境例外。一些替代方案是使用“bat wrappers”或ftype / assoc注册表黑客进行文件扩展。
Windows从DOS命令shell运行时忽略了shebang行,但具有讽刺意味的是在Apache for Windows中CGI-ing Perl时使用它。我厌倦了直接在我的网络程序中编码#!c:/perl/bin/perl.exe,因为移动到* nix环境时可移植性问题。相反,我在工作站上创建了一个c:\ usr \ bin目录,并从其默认位置复制了perl.exe二进制文件,对于AS Perl通常为c:\ perl \ bin,对于Strawberry Perl通常为c:\ strawberry \ perl \ bin。因此,在Windows上的Web开发模式中,我的程序在迁移到Linux / UNIX webhost时不会中断,我可以使用标准问题shebang line“#!/ usr / bin / perl -w”,而不必先使用SED疯狂部署。 :)
在DOS命令shell环境中,我只是显式设置了PATH,或者创建了一个指向带有嵌入式开关-Mperl5i的实际perl.exe二进制文件的ftype。 shebang线被忽略了。
ftype p5i=c:\strawberry\perl\bin\perl.exe -Mperl5i %1 %*
assoc .pl=p5i
然后从DOS命令行,您可以单独调用“program.pl”而不是“perl -Mperl5i program.pl”
因此,只需输入Perl程序本身的名称,“say”语句就可以在5.10中工作而无需任何额外的哄骗,并且它也会接受可变数量的命令行参数。
答案 3 :(得分:0)
使用CommandLineToArgvW构建您的argv,或者直接将命令行传递给CreateProcess。
当然,这需要一个单独的Windows特定解决方案,但你说你没关系,这是相对简单的,并且通常专门针对目标系统编写关键部分有助于集成(来自用户的POV) 。 YMMV。
如果您想使用和不使用控制台运行相同的程序,您应该阅读有关该主题的Raymond Chen。
答案 4 :(得分:0)
在Windows上,在系统级别,命令行作为单个UTF-16字符串传递给已启动的程序,因此在shell中输入的任何引号都将按原样传递。因此,不会删除示例中的双引号。这与POSIX世界完全不同,在这个世界中shell执行解析工作,并且启动的程序接收字符串数组。
我在这里描述了系统级别的行为。但是,在您的C(或您的Perl)程序之间通常有C标准库正在解析系统命令行字符串,以将其作为main()
或wmain()
提供给argv[]
。这是在您的进程内完成的,但如果您真的想要控制解析的完成方式,或者以完整的UTF-16编码获取字符串,您仍然可以使用GetCommandLineW()
访问原始命令行字符串。
要了解有关Windows命令行解析怪癖的更多信息,请阅读以下内容:
您可能还对Win32上wrapper I wrote的Padre代码感兴趣:这是一个GUI程序(这意味着如果从“开始”菜单启动它将无法打开控制台) padre.exe
嵌入perl以启动padre
Perl脚本。它还有一个小技巧:它更改argv[0]
以将其指向perl.exe
,以便$^X
可用于启动外部perl脚本。
您在示例代码中使用的execv
只是类似POSIX行为的C库中的仿真。特别是它不会在您的参数周围添加引号,以便启动的perl按预期工作。你必须自己做。
请注意,由于客户端负责解析,因此每个客户端客户端都可以按照自己的方式进行解析。许多让libc这样做,但不是全部。因此,Windows上的通用命令行生成规则不存在:规则取决于启动的程序。 您可能仍然对“尽力而为”的实施感兴趣,例如Win32::ShellQuote。
答案 5 :(得分:-1)
如果您能够使用C ++,那么Boost.Program_options可能会有所帮助:
http://www.boost.org/doc/libs/1_39_0/doc/html/program_options.html