2008-11-13 33 views
5

bir dize kaçış Yerel ağa göz atmak ve "system" kullanarak mplayer dosya adlarını geçmek için basit bir program yazıyorum. Ancak, bazen dosya adları boşluk veya tırnak içerir. Açıkçası, kendimden kaçmak için kendi işlevimi yazabilirim, ancak hangi karakterlerin kaçtığını veya kaçmasına gerek duymadığından emin değilim. güvenle komut satırına geçmek için bir dize kaçmayı CRT mevcut ya da bir yere linux başlıklarında bir işlevNasıl güvenli bir şekilde C++

var mı? ben bunu yapar bir işlev bilmiyorum iken

cevap

7

farklı kabuklar özel karakterler ne farklı fikirleri var ve nasıl yorumlanır, çünkü her yerde çalışır tek çözüm yoktur. Bash, büyük olasılıkla (ilk tek tırnak dizisi durur '"'"' ile dosya adında her alıntı değiştirdikten sonra tek tırnak içinde bütün dosya adı çevreleyen birlikte kaçmak, "'" dizeye literal tek alıntı ekler, nihai tek alıntı, alıntılanan diziyi tekrar başlatır). Daha iyi bir çözüm, programı kullanmadan programı çağırmak için bir yol bulmaktır; örneğin, exec işlevlerinden biriyle çatal kullanarak, herhangi bir kabuk enterpolasyonu yoktur.

+0

haklısın. sadece '' ama '' '' 'değil: p –

+0

Belirli bir kabuk için güvenli bir çözüm oluşturmak mümkün ancak sorulan soruya cevap verdim, bunun için en iyi yol olmadığını kabul ettim ve bir kasa sağladı alternatif. Bunun bir oy hakkı olduğunu düşünmüyorum. –

2

, sen '...' ile argümanlar her çevreleyen ve '"'"' orijinal argüman herhangi ' yerini alabilir. foo 'bar olan ve " veya \n gibi garip şeyler içermesine izin verilen tek bir argüman system("mplayer 'foo'\"'\"' bar'"); gibi tek bir argüman verecektir. Yukarıdaki "'dan (\") önce kaçmanın sadece geçerli C++ olmasını sağladığını unutmayın.

Her bir bağımsız değişkeni kabul eden bir işlevi kullanmayı düşünün, böylece bu tür sorunlardan kaçınmalısınız. Vikipedi, ünlü fork-and-exec modeli hakkında iyi bir makaleye sahiptir. http://en.wikipedia.org/wiki/Fork-exec

8

Diğer cevaplar bu çatal ve exec çözüm içerir, ancak ben bunu yapmak için tek doğru yol olduğunu iddia ederler. daha güvenli ve daha verimli bir parametre bağlama API'ler var olduğunda

kabuk argümanları Kaçan sadece SQL parametrelerini kaçmaya çalışırken olarak, böcek ve zaman kaybı eğilimli bir aptal fikir. İşte

örnek bir fonksiyonudur:

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; 
    } 
} 
+0

gerçekten doğru olan tek doğru yoldur. ama yine de, soru kabuk için C++ 'da kaçış şeklinizdi. İlk önce cevap verdik ve sonra nasıl doğru yapacağımızı gösterdik. –

+0

Bahsettiğim gibi, fork/exec bunu yapmanın daha iyi bir yoludur, fakat * bu soruyu yanıtladığımdan beri verdiğim bir kabuk için bunu güvenli bir şekilde ele almak mümkündür. Bunu yapmak için fork/exec bir seçenek değildi, bu yüzden her zaman bir zaman kaybı değildir. –

+0

Herkes şimdiden alıntıyı kapatmıştı, ben de fork/exec hakkında detay vermeye karar verdim ve aynı zamanda fikrimi de dile getirdim. Hiç kimseye amaçlanmadı. –

0

Ve şimdi burada kabuk kaçış sorununa tam bir çözümdür. Bu , kabuk için bir dize kaçma tam sorusunu yanıtlamıyor olsa da. Argümanları programa aktarma problemini çözer. Bu çözüm, komutlardan doğruca aktarılan argümanların komutlarını, bunlardan kurtulmak için endişelenmeden yürütmek için kullanılan bir POSIX portatif yoludur.

#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"); 

} 
İlgili konular