Запуск 'wc' с использованием execvp () распознает /home/usr/foo.txt, но не ~ / foo.txt - PullRequest
0 голосов
/ 29 августа 2018

У меня есть программа, в которой я хочу выполнить команду wc, используя execvp ().

Важные биты здесь:

char *argvNew[5];
argvNew[0] = "wc";
argvNew[1] = "/home/user/foo.txt";
argvNew[2] = NULL;
execvp(argvNew[0], arvgNew);

Это работает просто отлично, и терминал отображает вывод из wc. Однако, когда я изменяю строку на:

argvNew[1] = "~/foo.txt"

Я получаю эту ошибку в терминале:

wc: '~/foo.txt': No such file or directory

Запуск wc ~/foo.txt непосредственно из терминала ведет себя точно так же, как и запуск wc /home/user/foo.txt. Почему execvp имеет проблему с частью ~/?

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Когда вы вводите имя файла, например ~/foo.txt, в командной строке, оболочка раскрывает его для вас перед выполнением команды. Поэтому вы должны выполнить это расширение в своем коде C, если хотите, чтобы оно работало, или использовать оболочку, чтобы выполнить расширение для вас. Ни у функции execvp(), ни у команды wc нет проблем с ~; в текущем каталоге просто нет каталога с именем ~ (и, следовательно, в несуществующем каталоге нет файла с именем foo.txt).

Не так уж сложно написать код для выполнения работы (хотя для правильного выполнения в соответствии с POSIX требуется внимание к деталям - см. Расширение Tilde ).

Однако существуют также стандартные функции POSIX, которые могут помочь и выполнить эту работу, в частности wordexp(). Хотя в стандарте явно не упоминается расширение тильды, в нем упоминается WRDE_NOCMD для подавления подстановки команд , поэтому предполагается, что он должен выполнить тщательную работу, а расширение тильды должно быть включено в соответствующую реализацию.

Вот простой тестовый код, выполняющий эту функцию:

#include "stderr.h"
#include <stdio.h>
#include <wordexp.h>

/*
int wordexp(const char *restrict words, wordexp_t *restrict pwordexp,
       int flags);
void wordfree(wordexp_t *pwordexp);
*/

static void do_wordexp(const char *name)
{
    wordexp_t wx = { 0 };
    if (wordexp(name, &wx, WRDE_NOCMD) != 0)
        err_remark("Failed to expand word [%s]\n", name);
    else
    {
        printf("Expansion of [%s]:\n", name);
        for (size_t i = 0; i < wx.we_wordc; i++)
            printf("%zu: [%s]\n", i+1, wx.we_wordv[i]);
        wordfree(&wx);
    }
}

int main(int argc, char **argv)
{
    err_setarg0(argv[0]);

    if (argc <= 1)
        do_wordexp("~/.profile");
    else
    {
        for (int i = 1; i < argc; i++)
            do_wordexp(argv[i]);
    }
    return 0;
}

А вот несколько примеров прогонов (программа wexp19 построена из wexp19.c):

$ wexp19
Expansion of [~/.profile]:
1: [/Users/jonathanleffler/.profile]
$ wexp19 '~informix/bin/oninit' '~/bin/rfmt'  '~/bin/al ~/bin/b?' '~/bin/f?' '$IXD/bin/onstat' ' ~/bin/ow '
Expansion of [~informix/bin/oninit]:
1: [/Users/informix/bin/oninit]
Expansion of [~/bin/rfmt]:
1: [/Users/jonathanleffler/bin/rfmt]
Expansion of [~/bin/al ~/bin/b?]:
1: [/Users/jonathanleffler/bin/al]
2: [/Users/jonathanleffler/bin/bk]
Expansion of [~/bin/f?]:
1: [/Users/jonathanleffler/bin/fd]
2: [/Users/jonathanleffler/bin/fl]
3: [/Users/jonathanleffler/bin/fm]
Expansion of [$IXD/bin/onstat]:
1: [/opt/informix/12.10.FC6/bin/onstat]
Expansion of [ ~/bin/ow ]:
1: [/Users/jonathanleffler/bin/ow]
$

Обратите особое внимание на результат расширения аргумента командной строки, заключенного в кавычки '~/bin/al ~/bin/b?'; это разделено, и слова отдельно расширены. Следующий за последним аргументом также расширяет переменную окружения $IXD из моей среды, и в последнем примере показано, что пробелы удалены. Одиночные кавычки в командной строке необходимы, чтобы не дать оболочке делать то, что я хочу продемонстрировать wordexp().

Тесты выполняются на Mac с MacOS 10.13.6 High Sierra и с использованием GCC 8.2.0. YMMV!

0 голосов
/ 29 августа 2018

Расширение тильды (~) выполняется / понимается только оболочками - оно не распространяется в программах на Си. Поэтому execvp пытается интерпретировать ~ как часть имени файла (аргумент wc).

Что вы можете сделать, это получить значение каталога HOME, используя getenv, и добавить к имени файла префикс (например, используя snprintf).

...