【问题标题】:scanf(), Linux's shell input in handled differently, why?scanf(),Linux 的 shell 输入处理方式不同,为什么?
【发布时间】:2011-10-25 05:08:56
【问题描述】:

我得到了以下内容。

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() 能够以相同方式处理这两种情况的正确方法是什么?

【问题讨论】:

  • 这不应该发生。您正在实际代码中执行其他操作。我怀疑这是您实际上没有尝试过的“简化”。
  • 我不能再复制了,很奇怪。好像有点不正常。现在上面的情况正常。谢谢大家!
  • 使用""而不是"\0"来初始化你的字符数组更清楚。 (这与您的问题无关,只是一个风格点。)
  • 忽略 scanf 的返回值丢失了可能有用的信息。根据我的经验,scanf 对不完全符合预期的输入过于挑剔,大多数人最好忘记它的存在。

标签: c linux shell scanf


【解决方案1】:

这一行:

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】:

    巴兰金,

    嗯……这是一个有趣的行为。

    两种标准输入间接技术都对我有用(如预期的那样)......

    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 文件的末尾添加一个换行符?

    干杯。基思。

    【讨论】:

      【解决方案3】:

      检查您的 credentials.data 是否在末尾包含换行符? cat 命令自动将其附加到文件的最后一行之后。

      【讨论】:

      • 不需要换行,因为scanf 仍会收到EOF
      【解决方案4】:

      尽我所能,我无法重现与您相同的问题。我什至手动从credentials.data 文件中删除了行尾字节,它仍然可以正常工作(应该如此)。您正在运行什么版本的 Linux 或 shell?

      【讨论】:

        猜你喜欢
        • 2016-06-14
        • 1970-01-01
        • 2017-09-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-11
        • 1970-01-01
        • 2015-04-12
        相关资源
        最近更新 更多