C 使用wordexp时保留引号
我正在尝试使用C 使用wordexp时保留引号,c,linux,C,Linux,我正在尝试使用wordexp函数对一些字符串进行类似shell的扩展wordexp删除单引号和双引号,但我想保留它们。我最初的想法是用另一对引号(这次是转义的)包围输入字符串中的所有引号对,而wordexp应该保持不变(或者相反)。不幸的是 对于更复杂的输入,这将失败 例如,对于““TEST”“”,我想以\'\““TEST\'\'\“\'\'结束,我编写了以下代码片段来演示使用我的方法时实际发生的情况: #include <stdio.h> #include <wordexp.
wordexp
函数对一些字符串进行类似shell的扩展wordexp
删除单引号和双引号,但我想保留它们。我最初的想法是用另一对引号(这次是转义的)包围输入字符串中的所有引号对,而wordexp
应该保持不变(或者相反)。不幸的是
对于更复杂的输入,这将失败
例如,对于““TEST”“”
,我想以\'\““TEST\'\'\“\'\'
结束,我编写了以下代码片段来演示使用我的方法时实际发生的情况:
#include <stdio.h>
#include <wordexp.h>
static void expansion_demo(char const *str)
{
printf("Before expansion: %s\n", str);
wordexp_t exp;
wordexp(str, &exp, 0);
printf("After expansion: %s\n", exp.we_wordv[0]);
wordfree(&exp);
}
int main(void)
{
char const *str1 = "\\''\\\"\"\"\\\"TEST1\\\"\"\"\\\"'\\'";
expansion_demo(str1);
char const *str2 = "'\\'\"\\\"\\\"\"TEST2\"\\\"\\\"\"\\''";
expansion_demo(str2);
return 0;
}
这会失败,因为双引号嵌套在单引号中
在这种情况下,天真地用转义引号包围每对引号是行不通的(尽管我不知道为什么会发生segfault)
我还考虑过用其他ascii字符临时交换引号,但是没有任何引号不能作为有效shell命令的一部分
有没有办法让它适应我想要的?或者更简单的方法?分段错误
在代码中,第二个测试字符串:
char const *str2 = "'\\'\"\\\"\\\"\"TEST2\"\\\"\\\"\"\\''";
产生语法错误。在这样的字符串上处理C或shell转义规则有点可怕,但是您可以分析在字符串末尾有一个不匹配的单引号。将C字符串文字转换为字符串将产生:
'\'"\"\""TEST2"\"\""\''
分析时,关键字符由插入符号标记:
'\'"\"\""TEST2"\"\""\''
^^^^^ ^ ^^ ^^ ^ ^^ ^
12345 6 78 91 1 11 1
0 1 23 4
TEST2
是引号外的纯文本(字符串的一部分)wordexp()
的返回值是WRDE_syntax
,这说明了这一点。由于exp
结构在exp.we\u wordv
成员中设置了空指针,因此会出现分段错误
此更安全的代码版本演示了以下内容:
/* SO 5246-1162 */
#include <stdio.h>
#include <wordexp.h>
static const char *worderror(int errnum)
{
switch (errnum)
{
case WRDE_BADCHAR:
return "One of the unquoted characters - <newline>, '|', '&', ';', '<', '>', '(', ')', '{', '}' - appears in an inappropriate context";
case WRDE_BADVAL:
return "Reference to undefined shell variable when WRDE_UNDEF was set in flags to wordexp()";
case WRDE_CMDSUB:
return "Command substitution requested when WRDE_NOCMD was set in flags to wordexp()";
case WRDE_NOSPACE:
return "Attempt to allocate memory in wordexp() failed";
case WRDE_SYNTAX:
return "Shell syntax error, such as unbalanced parentheses or unterminated string";
default:
return "Unknown error from wordexp() function";
}
}
static void expansion_demo(char const *str)
{
printf("Before expansion: [%s]\n", str);
wordexp_t exp;
int rc;
if ((rc = wordexp(str, &exp, 0)) == 0)
{
for (size_t i = 0; i < exp.we_wordc; i++)
printf("After expansion %zu: [%s]\n", i, exp.we_wordv[i]);
wordfree(&exp);
}
else
printf("Expansion failed (%d: %s)\n", rc, worderror(rc));
}
int main(void)
{
char const *str1 = "\\''\\\"\"\"\\\"TEST1\\\"\"\"\\\"'\\'";
expansion_demo(str1);
char const *str2 = "'\\'\"\\\"\\\"\"TEST2\"\\\"\\\"\"\\''";
expansion_demo(str2);
return 0;
}
wordexp()
做什么
该函数的设计目的是(或多或少)执行与shell相同的扩展,如果将字符串作为命令行的一部分提供给shell。这里有一个简单的程序可以说明这一点。这是对-源文件wexp79.c
的改编
#include "stderr.h"
#include <stdio.h>
#include <stdlib.h>
#include <wordexp.h>
static const char *worderror(int errnum)
{
switch (errnum)
{
case WRDE_BADCHAR:
return "One of the unquoted characters - <newline>, '|', '&', ';', '<', '>', '(', ')', '{', '}' - appears in an inappropriate context";
case WRDE_BADVAL:
return "Reference to undefined shell variable when WRDE_UNDEF was set in flags to wordexp()";
case WRDE_CMDSUB:
return "Command substitution requested when WRDE_NOCMD was set in flags to wordexp()";
case WRDE_NOSPACE:
return "Attempt to allocate memory in wordexp() failed";
case WRDE_SYNTAX:
return "Shell syntax error, such as unbalanced parentheses or unterminated string";
default:
return "Unknown error from wordexp() function";
}
}
static void do_wordexp(const char *name)
{
wordexp_t wx = { 0 };
int rc;
if ((rc = wordexp(name, &wx, WRDE_NOCMD | WRDE_SHOWERR | WRDE_UNDEF)) != 0)
err_remark("Failed to expand word [%s]\n%d: %s\n", name, rc, worderror(rc));
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)
{
char *buffer = 0;
size_t buflen = 0;
int length;
while ((length = getline(&buffer, &buflen, stdin)) != -1)
{
buffer[length-1] = '\0';
do_wordexp(buffer);
}
free(buffer);
}
else
{
for (int i = 1; i < argc; i++)
do_wordexp(argv[i]);
}
return 0;
}
它将产生:
Expansion of [*.c]:
1: [esc11.c]
2: [so-5246-1162-a.c]
3: [so-5246-1162-b.c]
4: [wexp19.c]
5: [wexp79.c]
Expansion of [*[mM]*]:
1: [README.md]
2: [esc11.dSYM]
3: [makefile]
4: [so-5246-1162-b.dSYM]
5: [wexp19.dSYM]
6: [wexp79.dSYM]
Expansion of [*.[ch] *[mM]* ~/.profile $HOME/.profile]:
1: [esc11.c]
2: [so-5246-1162-a.c]
3: [so-5246-1162-b.c]
4: [wexp19.c]
5: [wexp79.c]
6: [README.md]
7: [esc11.dSYM]
8: [makefile]
9: [so-5246-1162-b.dSYM]
10: [wexp19.dSYM]
11: [wexp79.dSYM]
12: [/Users/jleffler/.profile]
13: [/Users/jleffler/.profile]
请注意它是如何扩展波浪符号和$HOME
的
转义字符串
看起来,您所追求的是保留字符串的代码,例如
'""TEST""'
通过外壳进行扩展,产生如下输出:
\''""TEST""'\'
我有一系列函数可以生成一个与之等价的字符串(尽管实际输出与我展示的不同;这些函数使用蛮力,上面的示例输出生成一个稍微简单的字符串)。此代码在GitHub上的my(Stack Overflow Questions)存储库中作为子目录中的文件escape.c
和escape.h
提供。下面是一个使用escape\u simple()
的程序,它将转义任何包含可移植文件名字符集以外字符的字符串([-a-Za-z0-9.,/])
)
正如我所指出的,转义码使用暴力。它输出一个单引号,然后处理字符串,将遇到的每个单引号替换为\''
。这一顺序:
- 结束当前单引号字符串
- 添加转义单引号(
)\'
- 开始(继续)单个带引号的字符串
$ printf "%s\n" ''\''""TEST""'\''' '\'\'''\''\"""\"TEST1\"""\"'\''\'\''' ''\''\'\''"\"\""TEST2"\"\""\'\'''\'''
'""TEST""'
\''\"""\"TEST1\"""\"'\'
'\'"\"\""TEST2"\"\""\''
$
没有办法声称任何shell代码都很容易阅读;阅读起来极其困难。但复制粘贴会让生活更轻松。
在代码中,第二个测试字符串:
char const *str2 = "'\\'\"\\\"\\\"\"TEST2\"\\\"\\\"\"\\''";
产生语法错误。在这样的字符串上处理C或shell转义规则有点可怕,但是您可以分析在字符串末尾有一个不匹配的单引号。将C字符串文字转换为字符串将产生:
'\'"\"\""TEST2"\"\""\''
分析时,关键字符由插入符号标记:
'\'"\"\""TEST2"\"\""\''
^^^^^ ^ ^^ ^^ ^ ^^ ^
12345 6 78 91 1 11 1
0 1 23 4
TEST2
是引号外的纯文本(字符串的一部分)wordexp()
的返回值是WRDE_syntax
,这说明了这一点。由于exp
结构在
$ printf "%s\n" ''\''""TEST""'\''' '\'\'''\''\"""\"TEST1\"""\"'\''\'\''' ''\''\'\''"\"\""TEST2"\"\""\'\'''\'''
'""TEST""'
\''\"""\"TEST1\"""\"'\'
'\'"\"\""TEST2"\"\""\''
$