【问题标题】:Running 'wc' using execvp() recognizes /home/usr/foo.txt but not ~/foo.txt使用 execvp() 运行 'wc' 可以识别 /home/usr/foo.txt 但不能识别 ~/foo.txt
【发布时间】:2019-02-04 16:20:30
【问题描述】:

我有一个程序,我想在其中使用 execvp() 运行命令 wc

重要的部分在这里:

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~/ 部分有问题?

【问题讨论】:

  • shell 在运行命令之前会为您扩展~/foo.txt。因此,如果您希望它工作,您必须进行该扩展 - 或使用 shell 为您进行扩展。 execvp~没有问题;当前目录中根本没有名为~ 的目录(因此在不存在的目录中没有名为foo.txt 的文件)。编写代码来完成这项工作并不是那么难(尽管按照 POSIX 正确执行它需要注意细节 - 请参阅 Tilde expansion)。
  • ~ 仅在 shell AFAIK 中扩展。所以它不会在自定义c 程序中扩展。我的意思是,shell 中的program ~/xxx 将扩展~ 而不是program

标签: c command exec


【解决方案1】:

波浪号 (~) 扩展仅由 shell 完成/理解 - 它不在 C 程序中扩展。所以execvp 尝试将~ 解释为文件名的一部分(wc 的参数)。

您可以做的是使用getenv 获取HOME 目录值并在其前面加上文件名(例如,使用snprintf)。

【讨论】:

    【解决方案2】:

    当您在命令行键入文件名(例如 ~/foo.txt)时,shell 会在运行命令之前为您展开它。因此,如果您希望它工作,您必须在您的 C 代码中进行该扩展——或者使用 shell 为您进行扩展。 execvp()函数和wc命令对~都没有问题;当前目录中根本没有名为~ 的目录(因此在不存在的目录中也没有名为foo.txt 的文件)。

    编写代码来完成这项工作并不难(尽管按照 POSIX 正确执行需要注意细节 - 请参阅 Tilde expansion)。

    但是,也有标准的 POSIX 函数可以帮助完成这项工作,特别是 wordexp()。虽然标准没有明确提到波浪号扩展,但它确实提到了WRDE_NOCMD 以抑制command substitution,因此它应该做一个彻底的工作,并且波浪号扩展应该包含在符合要求的实现中。

    下面是一些简单的测试代码来执行这个功能:

    #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,最后一个示例显示删除了空格。命令行中的单引号对于阻止 shell 执行我想要演示的 wordexp() 执行操作是必需的。

    测试在运行 macOS 10.13.6 High Sierra 并使用 GCC 8.2.0 的 Mac 上运行。 YMMV!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多