运行 'wc' 使用 execvp() 可以识别 /home/usr/foo.txt 但不能识别 ~/foo.txt
Running 'wc' using execvp() recognizes /home/usr/foo.txt but not ~/foo.txt
我有一个程序,我想使用 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
直接来自终端的行为与 运行ning wc /home/user/foo.txt
完全相同。为什么 execvp
的 ~/
部分有问题?
Tilde (~
) 扩展仅通过 shell done/understood - 它不会在 C 程序中扩展。因此 execvp
尝试将 ~
解释为文件名的一部分(wc
的参数)。
你可以做的是使用 getenv
获取 HOME
目录值并在其前面加上文件名(例如,使用 snprintf
)。
当您在命令行键入文件名(例如 ~/foo.txt
)时,shell 会在 运行 执行命令之前为您展开它。因此,如果你想让它工作,你必须在你的 C 代码中进行扩展——或者使用 shell 为你进行扩展。 execvp()
函数和wc
命令都没有问题~
;当前目录中根本没有名为 ~
的目录(因此在不存在的目录中没有名为 foo.txt
的文件)。
编写代码来完成这项工作并不难(尽管根据 POSIX 正确地完成它需要注意细节 — 参见 Tilde expansion)。
然而,也有标准的 POSIX 函数可以帮助完成这项工作,特别是 wordexp()
. Although the standard doesn't explicitly mention tilde expansion, it does mention WRDE_NOCMD
to suppress 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()
做的事情是必要的。
在 Mac 运行ning macOS 10.13.6 High Sierra 上测试 运行,并使用 GCC 8.2.0。 YMMV!
我有一个程序,我想使用 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
直接来自终端的行为与 运行ning wc /home/user/foo.txt
完全相同。为什么 execvp
的 ~/
部分有问题?
Tilde (~
) 扩展仅通过 shell done/understood - 它不会在 C 程序中扩展。因此 execvp
尝试将 ~
解释为文件名的一部分(wc
的参数)。
你可以做的是使用 getenv
获取 HOME
目录值并在其前面加上文件名(例如,使用 snprintf
)。
当您在命令行键入文件名(例如 ~/foo.txt
)时,shell 会在 运行 执行命令之前为您展开它。因此,如果你想让它工作,你必须在你的 C 代码中进行扩展——或者使用 shell 为你进行扩展。 execvp()
函数和wc
命令都没有问题~
;当前目录中根本没有名为 ~
的目录(因此在不存在的目录中没有名为 foo.txt
的文件)。
编写代码来完成这项工作并不难(尽管根据 POSIX 正确地完成它需要注意细节 — 参见 Tilde expansion)。
然而,也有标准的 POSIX 函数可以帮助完成这项工作,特别是 wordexp()
. Although the standard doesn't explicitly mention tilde expansion, it does mention WRDE_NOCMD
to suppress 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()
做的事情是必要的。
在 Mac 运行ning macOS 10.13.6 High Sierra 上测试 运行,并使用 GCC 8.2.0。 YMMV!