当您在命令行键入文件名(例如 ~/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!