我正处于一个两难境地,我正试图在C中通过popen传递字符串,但让它在字符串中保持双引号。该字符串如下:
ssh %s@%s grep -c \"%s\" %s%s
我需要运行此命令,以便从远程系统上的日志中获取grep并返回它的计数。我通过字符串传递不同的参数,因此它生成用户名和密码以及搜索字符串和目标。但是,搜索字符串包含由空格字符分隔的多个单词,因此我需要双引号完整,以便正确解析搜索字符串。但是,到目前为止,popen已经从字符串中删除了双引号,因此搜索无法完成。我不确定另一种方法来执行远程ssh命令,或者在语句中保留双引号。有什么想法吗?
谢谢!
*编辑:这是我用来生成字符串以及popen命令的完整sprintf语句。
sprintf(command, "ssh %s@%s grep -c \"%s\" %s%s", user, server, search, path, file);
fd = popen(command, "r");
答案 0 :(得分:2)
popen
分叉一个子进程,该子进程有2个参数:-c
和传递给popen的命令字符串。因此,您传递的任何对shell有意义的字符都需要进行转义,以便shell使用它们做正确的事情。在您的情况下,您需要shell来保留"
个字符,以便远程shell将获取它们,这最简单的方法是在它们周围包裹'
引号:
sprintf(command, "ssh %s@%s grep -c '\"%s\"' %s%s", ...
但是,只有当您的搜索字符串不包含任何'
或"
字符时,这才有效 - 如果有,则需要进行更复杂的转义。你可以使用反斜杠转义:
sprintf(command, "ssh %s@%s grep -c \\\"%s\\\" %s%s", ...
但如果您的搜索字符串有引号或多个连续的空格或其他空格(例如制表符),则此操作将失败。要处理所有情况,首先需要在搜索字符串中的所有相关字符之前插入反斜杠,加上'
是很难处理的:
// allocate 4*strlen(search)+1 space as escaped_search
for (p1 = search, p2 = escaped_search; *p1;) {
if (*p1 == '\'') *p2++ '\'';
if (strchr(" \t\r\n\"\\'{}()<>;&|`$", *p1))
*p2++ = '\';
if (*p1 == '\'') *p2++ '\'';
*p2++ = *p1++; }
*p2 = '\0';
sprintf(command, "ssh %s@%s grep -c '%s' %s%s", user, server, escaped_search, ...
答案 1 :(得分:0)
经过一些实验,这似乎可能就是你所需要的:
snprintf(command, sizeof(command), "ssh %s@%s grep -c \\\"%s\\\" %s%s",
username, hostname, search_string, directory, file);
您需要多个反斜杠,因为涉及多个反斜杠解释器。
command
字符串中嵌入双引号。然后,命令字符串包含\"
两次。以下是一些演示代码。本地程序称为pop
。那里的所有东西都是硬连线的,因为我很懒(但我把远程主机名与我实际测试的那个相比)。程序/u/jleffler/linux/x86_64/bin/al
列出了收到的参数,每行一个。我觉得这对于这样的情况来说是一个非常有用的工具。请注意,al
的参数是用双空格精心设计的,以显示参数被视为一对多的情况。
$ ./pop
Command: <<ssh jleffler@remote.example.com /u/jleffler/linux/x86_64/bin/al \"x y z\" \"pp qq rr\">>
jleffler@remote.example.com's password:
Response: <<x y z>>
Response: <<pp qq rr>>
$
#include <stdio.h>
#include <string.h>
int main(void)
{
char command[512];
char const *arg1 = "x y z";
char const *arg2 = "pp qq rr";
char const *cmd = "/u/jleffler/linux/x86_64/bin/al";
char const *hostname = "remote.example.com";
char const *username = "jleffler";
snprintf(command, sizeof(command), "ssh %s@%s %s \\\"%s\\\" \\\"%s\\\"",
username, hostname, cmd, arg1, arg2);
printf("Command: <<%s>>\n", command);
FILE *fp = popen(command, "r");
char line[512];
while (fgets(line, sizeof(line), fp) != 0)
{
line[strlen(line)-1] = '\0';
printf("Response: <<%s>>\n", line);
}
pclose(fp);
return(0);
}
$ ./pop1
Command: <<ssh jleffler@remote.example.com /u/jleffler/linux/x86_64/bin/al "x y z" "pp qq rr">>
jleffler@remote.example.com's password:
Response: <<x>>
Response: <<y>>
Response: <<z>>
Response: <<pp>>
Response: <<qq>>
Response: <<rr>>
$
#include <stdio.h>
#include <string.h>
int main(void)
{
char command[512];
char const *arg1 = "x y z";
char const *arg2 = "pp qq rr";
char const *cmd = "/u/jleffler/linux/x86_64/bin/al";
char const *hostname = "remote.example.com";
char const *username = "jleffler";
snprintf(command, sizeof(command), "ssh %s@%s %s \"%s\" \"%s\"",
username, hostname, cmd, arg1, arg2);
printf("Command: <<%s>>\n", command);
FILE *fp = popen(command, "r");
char line[512];
while (fgets(line, sizeof(line), fp) != 0)
{
line[strlen(line)-1] = '\0';
printf("Response: <<%s>>\n", line);
}
pclose(fp);
return(0);
}
$ ./pop2
Command: <<al jleffler@remote.example.com /u/jleffler/linux/x86_64/bin/al \"x y z\" \"pp qq rr\">>
Response: <<jleffler@remote.example.com>>
Response: <</u/jleffler/linux/x86_64/bin/al>>
Response: <<"x>>
Response: <<y>>
Response: <<z">>
Response: <<"pp>>
Response: <<qq>>
Response: <<rr">>
$
本地shell在双引号之前不需要反斜杠;事实上,添加它就错了。
#include <stdio.h>
#include <string.h>
int main(void)
{
char command[512];
char const *arg1 = "x y z";
char const *arg2 = "pp qq rr";
char const *cmd = "/u/jleffler/linux/x86_64/bin/al";
char const *hostname = "remote.example.com";
char const *username = "jleffler";
snprintf(command, sizeof(command), "al %s@%s %s \\\"%s\\\" \\\"%s\\\"",
username, hostname, cmd, arg1, arg2);
printf("Command: <<%s>>\n", command);
FILE *fp = popen(command, "r");
char line[512];
while (fgets(line, sizeof(line), fp) != 0)
{
line[strlen(line)-1] = '\0';
printf("Response: <<%s>>\n", line);
}
pclose(fp);
return(0);
}
$ ./pop3
Command: <<al jleffler@remote.example.com /u/jleffler/linux/x86_64/bin/al "x y z" "pp qq rr">>
Response: <<jleffler@remote.example.com>>
Response: <</u/jleffler/linux/x86_64/bin/al>>
Response: <<x y z>>
Response: <<pp qq rr>>
$
#include <stdio.h>
#include <string.h>
int main(void)
{
char command[512];
char const *arg1 = "x y z";
char const *arg2 = "pp qq rr";
char const *cmd = "/u/jleffler/linux/x86_64/bin/al";
char const *hostname = "remote.example.com";
char const *username = "jleffler";
snprintf(command, sizeof(command), "al %s@%s %s \"%s\" \"%s\"",
username, hostname, cmd, arg1, arg2);
printf("Command: <<%s>>\n", command);
FILE *fp = popen(command, "r");
char line[512];
while (fgets(line, sizeof(line), fp) != 0)
{
line[strlen(line)-1] = '\0';
printf("Response: <<%s>>\n", line);
}
pclose(fp);
return(0);
}
答案 2 :(得分:0)
问题不在sprintf
而在popen
。问题是调用ssh
的shell。它是剥去引号的shell。
您只需打开终端并手动尝试即可。你会看到
ssh user@server grep -c "search string" path
如果搜索字符串包含空格,则无法正常工作。你的shell会使用引号,因此最后grep
会收到没有引号的命令行,并且会错误地解释它。
如果希望引号保持不变,则必须为shell转义它们。您要执行的命令是
ssh user@server grep -c \"search string\" path
要使用sprintf
形成这样的字符串,您还必须转义"
和\
字符(对于C编译器),这意味着\"
变为\\\"
}。最终格式行如下
sprintf(command, "ssh %s@%s grep -c \\\"%s\\\" %s%s", user, server, search, path, file);
当然,这仍然会受到克里斯多德回答中提到的其他问题的困扰。
答案 3 :(得分:0)
正如其他人在此解释的那样,引用有时会变得很麻烦。
为了避免过多引用,只需将命令传递到ssh的标准输入中。如下面的bash代码:
ssh remote_host <<EOF
grep -c "search string" path
EOF