(C) scanf не работает, когда значение завершения [^ \ n] - PullRequest
2 голосов
/ 12 апреля 2020

Я создаю программу, в которой вы можете выполнять команды терминала через программу. Я хочу получить доступ к каталогу (используя cd / Users / user / Desktop), но поскольку scanf завершается с пробела, я был вынужден изменить значение завершения на [^\n]. Вот когда выскочила ошибка. Всякий раз, когда я ввожу команду, она выполняет указанную команду, но затем в следующий раз, когда программа проходит через (бесконечный) l oop, она не прекращает выполнение строки перед функцией scanf. Этого не произошло, когда конечное значение было %s. Вот код для программы:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void execute(){
    char* command = (char *) malloc(15);
    char* output = (char *) malloc(4096);
    printf(">> ");
    scanf("%[^\n]", command);            //Here is the scanf function
    FILE* cmd = popen(command, "r");
    fread(output, sizeof(output), 32000, cmd);
    if (strlen(output) != 0){
        printf("\n%s\n", output);
    }
    free(output);
    pclose(cmd);
}

int main(){
    while (1){
        execute();
    }
}

Здесь вывод, когда значение завершения [^\n]:

>> ls

Applications
Desktop
Documents
//Here the rest of the contents in my user folder appear (twice for some reason, that's also an issue related to this).

>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> 
>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> 
>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> 
>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> 
// This then goes on forever

, и вывод, когда значение завершения %s:

>> ls

Applications
Desktop
Documents
//Here the rest of the contents in my user folder appear (once)

>> //Here I can input things again

Может кто-нибудь показать мне, как это исправить? (Я пытался gets(), тот же результат)

Ответы [ 2 ]

3 голосов
/ 12 апреля 2020

Поскольку вы говорите scanf не читать \n, он оставляет его в stdin, поэтому, когда l oop повторяется, он все еще там и заставляет следующую итерацию немедленно возвращать пустую строку. Чтобы исправить это, есть несколько вариантов:

  • Измените "%[^\n]" на " %[^\n]", чтобы игнорировать начальные пробелы.
  • Добавьте getchar(); после scanf("%[^\n]", command);, чтобы использовать символ новой строки.
  • Используйте fgets или другую функцию чтения, отличную от scanf. (Не gets; его нельзя безопасно использовать!)

Вы также не проверяете, вернул ли scanf что-либо, поэтому, если он вообще ничего не анализировал, то вы ' передача неинициализированной памяти в popen.

. Примечание: в вашей программе много других ошибок, но они не имеют отношения к вашей непосредственной проблеме:

  1. Вы пропускаете память command каждый раз через ваш l oop. Чтобы исправить это, добавьте free(command); где-нибудь после popen и до конца функции.
  2. Вы не передали максимальную ширину поля для scanf, поэтому, если вы вводите более 14 символов , у вас будет переполнение буфера и повреждение памяти. Чтобы исправить это, измените %[^\n] на %14[^\n] (14, а не 15, чтобы было место для нулевого терминатора). Вы также должны обнаружить случай частичного чтения и правильно его обработать, чтобы избежать echo Their alarm is set удаления файлов, например, is и set.
  3. sizeof(output) будет размером с указатель, а не выделенная вами память, на которую он указывает, и 32000, кажется, вышел из воздуха. Измените sizeof(output) на 1 и 32000 на 4096.
  4. fread не завершит вывод на нуль, поэтому печать его с помощью %s напечатает неинициализированную память после нее. Чтобы исправить это, либо используйте что-то отличное от fread, чтобы получить выходные данные, либо используйте его возвращаемое значение и убедитесь, что вы печатаете только столько символов.

С вашей «фиксированной» версией есть еще несколько проблем:

  • Ваш первый fgets не будет переполняться буфером, но слишком длинные строки будут вызывать проблемы. В частности, если пользователь вводит слишком длинную команду, он будет действовать так, как если бы он нажал Enter в середине ее, что приведет к выполнению двух частичных команд.
  • Выполнение cd в подпроцессе не ' t влияет на родительский процесс или любые другие подпроцессы. Чтобы это работало, вам нужно проверить, начинается ли введенная команда с cd, и, если это так, вызывать chdir напрямую, а не popen.

Вот способ, который работает правильно:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(){
    char *command = NULL;
    size_t commandsize = 0;
    char *output = (char *)malloc(5000);
    while(1){
        printf(">> ");
        ssize_t commandlen = getline(&command, &commandsize, stdin);
        if(commandlen < 0) {
            // assume EOF. Small chance that it was an error though
            break;
        }
        printf("%s", command);
        if(!strcmp(command, "exit\n")) {
            break;
        }
        if(!strncmp(command, "cd ", 3)) {
            command[commandlen - 1] = '\0'; // remove the newline
            if(chdir(command + 3)) {
                perror("chdir");
            }
            continue;
        }
        FILE *cmd = popen(command, "r");
        if(!cmd) {
            perror("popen");
            continue;
        }
        size_t sz;
        while ((sz = fread(output, 1, 5000, cmd)) > 0){
            fwrite(output, 1, sz, stdout);
        }
        pclose(cmd);
    }
    free(command);
    free(output);
    return 0;
}

Несколько замечаний по этому поводу:

  • Вместо malloc и free. каждый раз через l oop я перемещал эти переменные в область действия функции, так что это происходит только один раз за все время работы программы.
  • Я использую getline, чтобы прочитать всю строку и автоматически выделить однако много места необходимо. Это, как и popen, не является частью стандартного C, но является частью POSIX.
  • Я использую fread и fwrite для передачи данных из процесса, который мы начали, чтобы избежать необходимости думать о нулевых терминаторах. Я предполагаю, что вы в конечном итоге собираетесь выполнить какую-то обработку этих данных; если нет, рассмотрите возможность использования system вместо popen, что автоматически запишет вывод пользователю.
0 голосов
/ 12 апреля 2020

Вот исправленный код (кроме проблемы cd) (еще один пользователь опубликовал окончательную версию кода. Его ответ был помечен как правильный ответ). игнорировать следующий код, он больше не имеет отношения :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(){

    while(1){
        char* command = (char *) malloc(30);
        char* output = (char *)  malloc(5000);
        printf(">> ");
        fgets(command, 30, stdin);
        printf("%s", command);
        FILE *cmd = popen(command, "r");
        while (fgets(output, 5000, cmd) != NULL){
            printf("%s", output);
        }
        pclose(cmd);
        free(command);
        free(output);
    }
    return 0;
}

...