如何从C ++安全地转义string

我正在编写一个简单的程序来浏览本地networking,并使用“system”将文件名传递给mplayer。 但是,有时文件名包含空格或引号。 显然,我可以编写自己的函数来逃避这些,但我不确定究竟哪些字符需要或不需要转义。

在CRT中还是有一个可用的函数来安全地转义string传递给命令行?

没有一个解决方案可以在任何地方使用,因为不同的shell对于特殊字符是什么以及如何解释是不同的。 对于bash,你可能会用单引号括起整个文件名,用'"'"'替换文件名中的每一个引号(第一个单引号停止序列, "'"将字面单引号附加到字符串,最后单引号再次引用引用的序列)。 一个更好的解决方案是找到一种方法来调用程序而不使用系统,比如通过使用fork来执行一个exec函数,所以没有shell插入。

其他答案包括这个fork和exec解决方案,但我声称这是唯一正确的方法来做到这一点。

转义shell参数容易出错并且浪费时间,正如尝试转义SQL参数在更安全和更高效的参数绑定API存在时是愚蠢的想法。

这是一个示例函数:

 void play(const char *path) { /* Fork, then exec */ pid = fork(); if( pid < 0 ) { /* This is an error! */ return; } if( pid == 0 ) { /* This is the child */ freopen( "/dev/null", "r", stdin ); freopen( "/dev/null", "w", stdout ); freopen( "/dev/null", "w", stderr ); execlp( "mplayer", "mplayer", path, (char *)0 ); /* This is also an error! */ return; } } 

虽然我不知道这样做的函数,但是可以用'...'来包围每个参数,并用'...'替换原始参数中的任何'"'"' 。 像system("mplayer 'foo'\"'\"' bar'"); 会给mplayer一个参数,这个参数是foo ,它允许包含奇怪的东西,例如"\n 。注意上面的( \" )之前的转义只是为了使它成为有效的C ++。

你应该考虑使用一个接受每个独立参数的函数,从而避免这样的问题。 维基百科有关这方面的着名的fork-and-exec模式有一篇很好的文章。 http://en.wikipedia.org/wiki/Fork-exec

而现在,这是一个完整的shell解决方案。 虽然这并没有回答为shell转义字符串的确切问题。 它解决了将参数传递给程序的问题。 这个解决方案是一个POSIX可移植的方式来执行正确传递到命令的参数,而不必担心需要转义它们。

 #include <cstdio> #include <cstdlib> #include <iostream> #include <sstream> #include <string> #include <sys/stat.h> #include <vector> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <string.h> std::vector<std::string> split(std::string delimiter, std::string str){ std::size_t nextPos = 0; std::size_t delimiterSize = delimiter.size(); std::vector<std::string> list; while(true){ std::size_t pos = str.find(delimiter, nextPos); std::string subStr; if(pos == std::string::npos){ list.push_back(str.substr(nextPos)); break; } subStr = str.substr(nextPos, pos - nextPos); list.push_back(subStr); nextPos = pos + delimiterSize; } return list; } bool isFileExecutable(const std::string &file) { struct stat st; if (stat(file.c_str(), &st) < 0) return false; if ((st.st_mode & S_IEXEC) != 0) return true; return false; } std::string ensureEndsWithSlash(std::string path){ if(path[path.length()-1] != '/'){ path += "/"; } return path; } std::string findProgram(std::string name){ // check if it's relative if(name.size() > 2){ if(name[0] == '.' && name[1] == '/'){ if(isFileExecutable(name)){ return name; } return std::string(); } } std::vector<std::string> pathEnv = split(":", getenv("PATH")); for(std::string path : pathEnv){ path = ensureEndsWithSlash(path); path += name; if(isFileExecutable(path)){ return path; } } return std::string(); } // terminal condition void toVector(std::vector<std::string> &vector, const std::string &str){ vector.push_back(str); } template<typename ...Args> void toVector(std::vector<std::string> &vector, const std::string &str, Args ...args){ vector.push_back(str); toVector(vector, args...); } int waitForProcess(pid_t processId){ if(processId == 0){ return 0; } int status = 0; int exitCode = -1; while(waitpid(processId, &status, 0) != processId){ // wait for it } if (WIFEXITED(status)) { exitCode = WEXITSTATUS(status); } return exitCode; } /** Runs the process and returns the exit code. You should change it so you can detect process failure vs this function actually failing as a process can return -1 too @return -1 on failure, or exit code of process. */ template<typename ...Args> int mySystem(Args ...args){ std::vector<std::string> command; toVector(command, args...); command[0] = findProgram(command[0]); if(command[0].empty()){ // handle this case by returning error or something // maybe std::abort() with error message return -1; } pid_t pid = fork(); if(pid) { // parent wait for child return waitForProcess(pid); } // we are child make a C friendly array // this process will be replaced so we don't care about memory // leaks at this point. std::vector<char*> c_command; for(int i = 0; i < command.size(); ++i){ c_command.push_back(strdup(command[i].c_str())); } // null terminate the sequence c_command.push_back(nullptr); execvp(c_command[0], &c_command[0]); // just incase std::abort(); return 0; } int main(int argc, char**argv){ // example usage mySystem("echo", "hello", "world"); }