Написание переносимой командной строки в C

Я пишу модуль perl под названием perl5i. Его цель - исправить ряд общих проблем 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 "[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 усложняет этот подход. По-видимому, программы в Windows не передают массив аргументов, они передают все аргументы как одну строку, а затем выполняют собственный анализ. Таким образом, что-то вроде perl5i -e "say 'Hello'" становится perl -Mperl5i -e say 'Hello', и Windows не может справиться с отсутствием цитирования.

Итак, как я могу справиться с этим? Обернуть все в кавычки и ускользнуть в Windows? Есть ли библиотека для обработки этого для меня? Есть ли лучший подход? Могу ли я просто не создавать C-программу в Windows и записывать ее как оболочку perl, поскольку она не поддерживает #! в любом случае?

ОБНОВЛЕНИЕ. Будьте более ясны, это отгруженное программное обеспечение, поэтому решения, требующие использования определенной оболочки или настройки конфигурации оболочки (например, alias perl5i='perl -Mperl5i'), не являются удовлетворительными.

Ответ 1

Для Windows используйте пакетный файл.

perl5i.bat

@echo off
perl -Mperl5i %*

%* - это все параметры командной строки минус %0.

В системах Unixy будет достаточно аналогичной оболочки script.

Update:

Я думаю, что это сработает, но я не мастер оболочки, и у меня нет системы * nix, которую можно протестировать.

perl5i

#!bash

perl -Mperl5i [email protected]

Обновить снова:

ДУХ! Теперь я правильно понял ваш комментарий #!. Моя оболочка script будет работать из CLI, но не в строке #!, так как #!foo требует, чтобы foo был двоичным файлом.

Отмените предыдущее обновление.

Кажется, что Windows все усложняет. Я считаю, что лучше всего использовать командный файл.

Вы можете использовать ассоциацию файлов, связать .p5i с perl -Mperl5i %*. Разумеется, это означает, что в реестре есть ошибки, которых лучше избегать ИМО. Лучше включить инструкции о том, как вручную добавить ассоциацию в свои документы.

Еще одно обновление

Вы можете посмотреть, как parl делает это.

Ответ 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.

Ответ 3

Windows всегда является нечетным случаем. Лично я бы не пытался кодировать исключение среды Windows. Некоторые альтернативы используют "обматывающие обертки" или ftype/assoc для взлома реестра для расширения файла.

Windows игнорирует строку shebang при запуске из командной оболочки DOS, но по иронии судьбы использует ее, когда CGI-Perl в Apache для Windows. Я устал от кодирования #! C:/perl/bin/perl.exe непосредственно в своих веб-программах из-за проблем с переносимостью при переходе в среду * nix. Вместо этого я создал каталог c:\usr\bin на моей рабочей станции и скопировал двоичный файл perl.exe из своего местоположения по умолчанию, обычно c:\perl\bin для AS Perl и c:\strawberry\perl\bin для Strawberry Perl. Таким образом, в режиме веб-разработки в Windows мои программы не будут разбиваться при переносе на хост Linux/UNIX, и я мог бы использовать стандартную строку строки "#!/Usr/bin/perl -w", к развертыванию.:)

В среде оболочки DOS я просто либо устанавливаю свой PATH явно, либо создаю ftype, указывающий на фактический двоичный файл perl.exe со встроенным коммутатором -Mperl5i. Строка shebang игнорируется.

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

Затем из командной строки DOS вы можете просто вызвать "program.pl" самостоятельно, а не "perl -Mperl5i program.pl"

Таким образом, оператор "say" работал в 5.10 без какого-либо дополнительного уговоров, просто введя имя самой программы Perl и также принял переменное количество аргументов командной строки.

Ответ 4

Используйте CommandLineToArgvW для создания вашего argv или просто передайте свою командную строку непосредственно CreateProcess.

В этом случае для этого требуется отдельное решение для Windows, но вы сказали, что с ним все в порядке, это относительно просто, и часто ключевые элементы кодирования, специально предназначенные для целевой системы, помогают интеграции (из POV пользователей) значительно, YMMV.

Если вы хотите запустить ту же программу как с консолью, так и без нее, вы должны прочитать Raymond Chen по этой теме.

Ответ 5

В Windows на системном уровне командная строка передается запущенной программе как одна строка UTF-16, поэтому любые кавычки, введенные в оболочку, передаются как есть. Таким образом, двойные кавычки из вашего примера не удаляются. Это сильно отличается от мира POSIX, где оболочка выполняет синтаксический анализ, а запущенная программа получает массив строк.

Я описал здесь поведение на уровне системы. Тем не менее, между вашей программой C (или вашим Perl) обычно существует стандартная библиотека C, которая анализирует строку командной строки системы, чтобы дать ей main() или wmain() как argv[]. Это делается внутри вашего процесса, но вы все равно можете получить доступ к исходной строке командной строки с помощью GetCommandLineW(), если вы действительно хотите контролировать, как синтаксический анализ или получить строку в полной кодировке UTF-16.

Чтобы узнать больше об ошибках синтаксического анализа командной строки Windows, прочитайте следующее:

Вам также может быть интересен код обертки, которую я написал для Padre на Win32: это GUI-программа (это означает, что она не будет открывать консоль, если она запущена из меню "Пуск" ) под названием padre.exe, которая включает perl для запуска padre Perl script, Он также делает небольшой трюк: он меняет argv[0], чтобы указать его на perl.exe, так что $^X будет чем-то полезным для запуска внешних скриптов perl.

execv, который вы используете в своем примере, является просто эмуляцией в библиотеке C поведения, подобного POSIX. В частности, он не будет добавлять кавычки вокруг ваших аргументов, чтобы запущенный perl работал так, как ожидалось. Вы должны сделать это сами.

Обратите внимание, что из-за того, что клиент отвечает за разбор, каждый клиентский клиент может делать это так, как он хочет. Многие позволяют libc делать это, но не все. Таким образом, общие правила генерации командной строки для Windows не могут существовать: правило зависит от запущенной программы. Вы все еще можете быть заинтересованы в реализации "лучших усилий", например Win32:: ShellQuote.