Et maintenant est ici une solution complète au problème d'échappement du shell. Bien que ce ne répond pas à la question exacte d'échapper une chaîne pour shell. Il résout le problème de passer des arguments au programme. Cette solution est un moyen POSIX portable d'exécuter des commandes avec des arguments correctement passés dans la commande sans se soucier de devoir les échapper.
#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");
}
vous avez raison. ce n'est pas seulement '' mais '' '' '': p –
Il est possible de créer une solution sûre pour un shell spécifique mais j'ai répondu à la question posée, j'ai reconnu que ce n'était pas la meilleure façon d'y arriver et j'ai fourni un alternative. Je ne pense pas que cela mérite un vote négatif. –