2009-07-07 18 views
11

Estoy escribiendo un módulo perl llamado perl5i. Su objetivo es arreglar una serie de problemas comunes de Perl en un módulo (usando muchos otros módulos).Escribiendo un contenedor de línea de comando portátil en C

para alegar ésta en la línea de comandos para los trazadores de líneas uno Usted escribiría: perl -Mperl5i -e 'say "Hello"' Creo que es demasiado prolijos así que me gustaría para suministrar una envoltura perl5i para que pueda escribir perl5i -e 'say "Hello"'. También me gustaría que las personas puedan escribir scripts con #!/usr/bin/perl5i, por lo que debe ser un programa C compilado.

Pensé que todo lo que tenía que hacer era presionar "-Mperl5i" en la parte delantera de la lista de argumentos y llamar a Perl. Y eso es lo que intenté.

#include <unistd.h> 
#include <stdlib.h> 

/* 
* Meant to mimic the shell command 
*  exec perl -Mperl5i "[email protected]" 
* 
* 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 complica este enfoque. Aparentemente, los programas en Windows no pasan una matriz de argumentos, se pasan todos los argumentos como una sola cadena y luego hacen su propio análisis. Por lo tanto, algo así como perl5i -e "say 'Hello'" se convierte en perl -Mperl5i -e say 'Hello' y Windows no puede ocuparse de la falta de cotización.

Entonces, ¿cómo puedo manejar esto? Ajustar todo entre comillas y escapes en Windows? ¿Hay una biblioteca para manejar esto por mí? ¿Hay un mejor enfoque? ¿Podría simplemente no generar un programa C en Windows y escribirlo como un contenedor perl ya que no es compatible con #! ¿de todas formas?

ACTUALIZACIÓN: Sea más claro, este software se envía por lo que las soluciones que requieren el uso de un cierto shell o ajustar la configuración del shell (por ejemplo, alias perl5i='perl -Mperl5i') no son satisfactorias.

Respuesta

3

Para Windows, utilice un archivo por lotes.

perl5i.bat

@echo off 
perl -Mperl5i %* 

%* es todos los parámetros de línea de comandos menos %0.

En los sistemas Unixy, un script de shell similar será suficiente.

Actualización:

creo que esto va a funcionar, pero no soy asistente de concha y no tengo un sistema * nix útil para probar.

perl5i

#!bash 

perl -Mperl5i [email protected] 

actualización de nuevo:

DUH! Ahora entendí su comentario #! correctamente. Mi script de shell funcionará desde la CLI pero no en una línea #!, ya que #!foo requiere que foo sea un archivo binario.

Sin tener en cuenta la actualización anterior.

Parece que Windows lo complica todo. Creo que lo mejor que puede hacer es usar un archivo por lotes.

Puede use a file association, asociado .p5i con perl -Mperl5i %*. Por supuesto, esto significa perder el registro, lo que es mejor evitar IMO. Es mejor incluir instrucciones sobre cómo agregar manualmente la asociación en sus documentos.

otra actualización

Es posible que desee ver cómo parl lo hace.

+1

Gracias, podría simplemente despejar y usar un archivo por lotes para Windows. ¡Un script de shell no será suficiente para Unix porque #! solo honrará los programas compilados. – Schwern

+3

Sin embargo, una desventaja de los archivos por lotes en Windows -^C-ing de una secuencia de comandos por lotes provocará un aviso de "Terminar proceso por lotes". Y para scripts de envoltura simples como este, el aviso es casi inútil, ya que incluso si eliges "N", aún así termina perl. – MiffTheFox

+0

En Unix su shell debe tener alguna forma de función de alias que hace que sea innecesario crear un script de shell. – jmucchiello

2

No puedo reproducir el comportamiento de su describen:

/* 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 

Eso es con Visual Studio 2005.

+0

Pruebe args entre comillas como '-e', 'hola' "'y observe cómo se dividen. – Schwern

+0

Sí, pero eso es normal para el shell de Windows. No veo cómo eso hace que tu envoltorio sea diferente del perl mismo. – hexten

+0

El contenedor recibe "-e", "diga 'hola'" en argv. Las comillas que rodean "decir 'hola'" han sido despojadas por el caparazón. Si luego lo pasa a otro programa, obtendrá "-e say 'hello'", que Windows no sabe cómo tratar. El contenedor (más probablemente el shell) quita las comillas para que el programa interno no tenga el beneficio de ellas. Lo verías si tu programa intentara llamar a otro con argv. – Schwern

0

Windows es siempre el caso extraño. Personalmente, no intentaría codificar para la excepción de entorno de Windows. Algunas alternativas son el uso de "bat wrappers" o ftype/assoc Registry hacks para una extensión de archivo.

Windows omite la línea shebang cuando se ejecuta desde una consola de comandos de DOS, pero irónicamente lo utiliza cuando CGI-ing Perl en Apache para Windows. Me cansé de codificar #! C: /perl/bin/perl.exe directamente en mis programas web debido a problemas de portabilidad cuando me moví a un entorno * nix. En lugar de ello he creado un directorio c: bin \ usr \ en mi estación de trabajo y copiar el binario perl.exe de su ubicación predeterminada, normalmente C: \ Perl \ bin para AS Perl y C: \ fresa \ Perl \ bin para Strawberry Perl. Así, en el modo de desarrollo web en Windows mis programas podrían no romperse cuando migrado a un servicio de hosting Linux/UNIX, y podría utilizar una línea de asunto tinglado estándar "#!/Usr/bin/perl -w" sin tener que pasar antes SED loca a la implementación. :)

En el entorno del shell de comandos de DOS, simplemente establezco mi ruta explícitamente o creo un ftype que apunta al binario real de perl.exe con el interruptor integrado -Mperl5i. La línea shebang es ignorada.

ftype p5i=c:\strawberry\perl\bin\perl.exe -Mperl5i %1 %* 
assoc .pl=p5i 

Entonces, desde la línea de comandos de DOS sólo se puede llamar "program.pl" por sí mismo en lugar de "perl -Mperl5i program.pl"

Así que el "decir" declaración trabajó en 5,10 sin ningún adicional persuadir simplemente ingresando el nombre del programa Perl, y también aceptaría una cantidad variable de argumentos de línea de comando.

0

Usa CommandLineToArgvW para construir tu argv, o simplemente pasa tu línea de comando directamente al CreateProcess.

Por supuesto, esto requiere una solución separada específica de Windows, pero dijiste que estás de acuerdo con eso, esto es relativamente simple, y la codificación de piezas clave específicamente para el sistema de destino ayuda a la integración (del POV de los usuarios) significativamente. YMMV.

Si desea ejecutar el mismo programa con y sin una consola, debe leer Raymond Chen sobre el tema.

0

En Windows, a nivel del sistema, la línea de comandos se pasa al programa puesto en marcha como una sola cadena UTF-16, por lo que cualquier cita introducidos en la cáscara se pasan como es. Por lo tanto, las comillas dobles de su ejemplo no se eliminan. Esto es bastante diferente del mundo POSIX donde el shell hace el trabajo de analizar y el programa lanzado recibe una matriz de cadenas.

que he descrito aquí el comportamiento a nivel del sistema.Sin embargo, entre el C (o su Perl) programa por lo general hay la biblioteca estándar de C que se analizar la cadena de línea de comandos del sistema para darle a main() o wmain() como argv[]. Esto se realiza dentro de su proceso, pero todavía se puede acceder a la cadena de línea de comandos original con GetCommandLineW() si realmente desea controlar cómo se realiza el análisis sintáctico, o conseguir la cadena en toda su codificación UTF-16.

Para obtener más información sobre las peculiaridades de línea de comandos de análisis de Windows, lea la siguiente:

Usted también podría estar interesado por el código de la wrapper I wrote para Padre en Win32: este es un programa de interfaz gráfica de usuario (lo que significa que no va a abrir una consola si se inicia desde el menú Inicio) llamado padre.exe que incrusta perl para lanzar elPerl script. También hace un pequeño truco: cambia argv[0] que apuntar a fin de que perl.exe$^X habrá algo utilizable para poner en marcha los scripts de Perl externos.

El execv que está utilizando en su código de ejemplo es solo una emulación en la biblioteca C del comportamiento similar a POSIX. En particular, no agregará comillas alrededor de sus argumentos para que el Perl lanzado funcione como se espera. Tienes que hacer eso tú mismo.

Tenga en cuenta que debido al hecho de que el cliente es responsable de analizar, cada cliente cliente puede hacerlo de la manera que quiera. Muchos dejan que la libc lo haga, pero no todos. Entonces, las reglas genéricas de generación de línea de comando en Windows no pueden existir: la regla depende del programa que se inicie. Puede que aún le interese la implementación del "mejor esfuerzo", como Win32::ShellQuote.

Cuestiones relacionadas