scanf (), ввод в оболочку Linux обрабатывается по-другому, почему? - PullRequest
4 голосов
/ 25 октября 2011

Я получил следующее.

my-app.c файл:

char username[USERNAME_MAX_LEN] = "\0";
char password[PASSWORD_MAX_LEN] = "\0";
scanf("%s %s", username, password);
printf("username-len: %ld, password-len: %ld\n", strlen(username), strlen(password));

credentials.data файл:

jdons f4$dsef35fs

Итак:

$ ./my-app < credentials.data
username-len: 0, password-len: 0

и:

$ cat credentials.data | ./my-app
username-len: 5, password-len: 10
  • Почему в обоих случаях ввод обрабатывается по-разному?В чем разница?
  • Как правильно использовать scanf(), чтобы можно было обрабатывать оба случая одинаково?

Ответы [ 4 ]

3 голосов
/ 25 октября 2011

Эта строка:

scanf("%s %s", username, password);

по своей сути небезопасна (если только у вас нет полного контроля над тем, что будет отображаться на стандартном входе вашей программы).Формат "%s" гласит, что нужно прочитать произвольно длинную последовательность непробельных символов.Как бы ни был длинен целевой массив, достаточно длинное слово (скажем, вызванное тем, что ваша кошка сидит на клавиатуре) переполнит его.

Вы можете использовать модификатор длины, чтобы ограничить размер ввода.Например (не проверено):

scanf("%36s %36s", username, password);

или, что лучше:

scanf("%*s, %*s", USERNAME_MAX_LEN, username, PASSWORD_MAX_LEN, password);

Но, вероятно, лучше использовать fgets(), чтобы прочитать всю строку за раз, а затем использовать,скажем, sscanf() для обработки строки после того, как вы ее прочитали.

И в вашем вызове printf есть возможная проблема:

printf("username-len: %ld, password-len: %ld\n",
       strlen(username),
       strlen(password));

strlen() возвращает результатТип size_t, но "%ld" требует аргумент типа long int.Если ваша система поддерживает это, вы можете использовать "%zu" для вывода значения типа size_t, но это не на 100% переносимо.Или вы можете преобразовать значение size_t, скажем, в unsigned long:

printf("username-len: %lu, password-len: %lu\n",
       (unsigned long)strlen(username),
       (unsigned long)strlen(password));

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

2 голосов
/ 25 октября 2011

Баранкин,

Хммм ... это интересное поведение.

Обе стандартные методики косвенного ввода для меня работают (как и ожидалось) ...

landkrc@lasun175:/home/user/landkrc/crap
$ cat lg.c
#include <stdio.h>
#include <strings.h>

#define USERNAME_MAX_LEN 36
#define PASSWORD_MAX_LEN 36

int main(int argc, char *argv[])
{
        printf("Hello, world\n");
        char username[USERNAME_MAX_LEN]; 
        char password[PASSWORD_MAX_LEN]; 
        *username = 0;
        *password = 0;
        scanf("%s %s", username, password); 
        printf("username-len: %ld, password-len: %ld\n", strlen(username), strlen(password)); 
        return 0;
}

landkrc@lasun175:/home/user/landkrc/crap
$ cc -V       
cc: Sun C 5.8 2005/10/13
usage: cc [ options] files.  Use 'cc -flags' for details

landkrc@lasun175:/home/user/landkrc/crap
$ cc -o lg lg.c

landkrc@lasun175:/home/user/landkrc/crap
$ echo '12345678 1234567890
> ' >data.txt

landkrc@lasun175:/home/user/landkrc/crap
$ lg <data.txt
username-len: 8, password-len: 10

landkrc@lasun175:/home/user/landkrc/crap
$ cat data.txt | lg
username-len: 8, password-len: 10

Может быть, вам просто нужен символ конца строки в конце вашего credentials.data файла?

Приветствия. Кит.

0 голосов
/ 25 октября 2011

Как бы я ни старался, мне не удается воспроизвести ту же проблему, что и вы. Я даже вручную удалил байт конца строки из файла credentials.data, и он все еще работает нормально (как и должно быть). Какую версию Linux или оболочки вы используете?

0 голосов
/ 25 октября 2011

Проверьте, содержит ли ваш credentials.data символ новой строки в конце? Команда cat автоматически добавляет его после последней строки файла.

...