Intereting Posts

Сохранение двойных кавычек при передаче строки в папку C

У меня возникла дилемма, когда я пытаюсь передать строку через popen в C, но она поддерживает двойные кавычки в строке. Строка читается следующим образом:

ssh %s@%s grep -c \"%s\" %s%s 

Мне нужно запустить эту команду, чтобы она загоралась из журнала в удаленной системе и возвращает счетчик для него. Я передаю разные аргументы через строку, поэтому он генерирует имя пользователя и пароль, а также строку поиска и цель. Тем не менее, строка поиска содержит несколько слов, разделенных пробельными символами, поэтому мне нужны двойные кавычки без изменений, чтобы они правильно разбирали строку поиска. Тем не менее, до сих пор popen удаляет двойные кавычки из строки, поэтому поиск не завершается. Я не уверен в другом способе выполнения удаленной команды ssh или сохранения двойных кавычек в инструкции. Есть идеи?

Спасибо!

* EDIT: Вот полный оператор sprintf, который я использую для создания строки, а также команды popen.

 sprintf(command, "ssh %s@%s grep -c \"%s\" %s%s", user, server, search, path, file); fd = popen(command, "r"); 

    popen создает дочерний элемент, который запускает оболочку с двумя аргументами: -c и командной строки, которую вы передали в popen. Таким образом, любые символы, которые вы передаете, которые имеют смысл для оболочки, должны быть экранированы, поэтому shell делает с ними что-то нужное. В вашем случае вам понадобится shell, чтобы ХРАНИТЬ символы, чтобы удаленная shell их получала, что проще всего сделать, обернув вокруг себя кавычки:

     sprintf(command, "ssh %s@%s grep -c '\"%s\"' %s%s", ... 

    Однако это будет работать только в том случае, если ваша строка поиска не содержит никаких символов ' или " Если это так, вам нужно выполнить более сложное экранирование. Вместо этого вы можете использовать обратную косую черту:

     sprintf(command, "ssh %s@%s grep -c \\\"%s\\\" %s%s", ... 

    но это не удастся, если ваша строка поиска содержит кавычки или несколько последовательных пробелов или других пробелов, таких как вкладки int it. Чтобы справиться со всеми случаями, вам нужно сначала вставить обратную косую черту перед всеми соответствующими символами в строке поиска, плюс ' это боль, с которой приходится иметь дело:

     // 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, ... 

    Проблема не в sprintf а не в popen . Проблема заключается в оболочке, которая вызывает ssh . Это shell, которая разделяет ваши кавычки.

    Вы можете просто открыть терминал и попробовать его вручную. Вы увидите, что

     ssh user@server grep -c "search string" path 

    не работает должным образом, если строка поиска содержит пробелы. Ваша shell расходует кавычки, так что в конце grep получает свою командную строку без кавычек и неправильно интерпретирует ее.

    Если вы хотите, чтобы кавычки сохранялись, вы должны избежать их для оболочки. Команда, которую вы хотите выполнить, – это

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

    Конечно, это может по-прежнему страдать от других проблем, упомянутых в ответе Криса Додда.

    После некоторых экспериментов это, вероятно, будет тем, что вам нужно:

     snprintf(command, sizeof(command), "ssh %s@%s grep -c \\\"%s\\\" %s%s", username, hostname, search_string, directory, file); 

    Вам нужно несколько обратных косых черт, потому что задействованы несколько интерпретаторов обратных косых черт.

    1. Компилятор C: он обрабатывает первые две обратные слэши в каждой последовательности из трех в качестве обратной косой черты. Третья обратная косая черта исключает двойную кавычку, вставляя двойную кавычку в command строку. Затем строка команды содержит \" дважды.
    2. Существует еще один процесс обработки обратных косых черт, и немного сложно определить, где он находится, но они необходимы для получения правильного результата, как показано экспериментами.

    Рабочий код – удаленное выполнение, правильный результат

    Вот какой демо-код работает. Локальная программа называется pop . Все в нем жестко связано, потому что я ленив (но я искал удаленное имя хоста по сравнению с тем, с которым я действительно тестировался). Программа /u/jleffler/linux/x86_64/bin/al перечисляет свои аргументы как полученные, по одному в каждой строке. Я считаю это очень полезным инструментом для подобных ситуаций. Обратите внимание, что аргументы в al тщательно обрабатываются с помощью двойных пробелов, чтобы показать, когда аргументы рассматриваются как один против многих.

     $ ./pop Command: <> jleffler@remote.example.com's password: Response: <> Response: <> $ 

    Код

     #include  #include  int main(void) { char command[512]; char const *arg1 = "xyz"; 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); } 

    Вариант 1 – Удаленное выполнение, неправильный результат

     $ ./pop1 Command: <> jleffler@remote.example.com's password: Response: <> Response: <> Response: <> Response: <> Response: <> Response: <> $ 

    Код

     #include  #include  int main(void) { char command[512]; char const *arg1 = "xyz"; 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); } 

    Вариант 2 – Локальное исполнение, (другое) Неверный результат

     $ ./pop2 Command: <> Response: <> Response: <> Response: <<"x>> Response: <> Response: <> Response: <<"pp>> Response: <> Response: <> $ 

    Локальной оболочке не требуется обратная косая черта перед двойной кавычкой; на самом деле, добавив, что это неправильно.

    Код

     #include  #include  int main(void) { char command[512]; char const *arg1 = "xyz"; 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); } 

    Вариант 3 – Локальное исполнение, правильный результат

     $ ./pop3 Command: <> Response: <> Response: <> Response: <> Response: <> $ 

    Код

     #include  #include  int main(void) { char command[512]; char const *arg1 = "xyz"; 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); } 

    Как объяснили другие, цитирование иногда становится громоздким.

    Чтобы избежать чрезмерного цитирования, просто соедините команды с стандартным входом ssh. Как и в следующем коде bash:

     ssh remote_host <