【问题标题】:Why is snprintf not reading after %s?为什么 snprintf 在 %s 之后不读取?
【发布时间】:2020-11-08 15:13:45
【问题描述】:

我正在尝试编写一个程序来检查 /home/user 目录下是否存在某个位置。为此,我必须使用whoami 命令获取用户名,并将其输出添加到缓冲区以使用locate 命令。

然而,即使snprintf 读取了whoami 命令,它也没有读取其余部分。我进行了几次搜索,结果NULL 可能不会在字符串末尾终止。不过,我不知道如何手动终止它。我不确定问题是什么,所以,我在这里。

这是更好地演示我的问题的代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>
    
char *readFile(char *);
bool check();
    
bool check() {
    char path[200] = { 0 };
    
    snprintf(path, 200, "/home/%s/.example", readFile("whoami"));
    
    char lll[300] = { 0 };
    
    snprintf(lll, 300, "locate %s", path);
    
    char *buffer = readFile(lll);
    
    if (strcmp(buffer, path) == 0) {
        return true;
    } else {
        return false;
    }
}

char *readFile(char cmd[200]) {
    char cmd1[99999] = { 0 };
    system("touch cmd");  
    snprintf(cmd1, 99999, "%s >> cmd", cmd);
    system(cmd1); 
        
    FILE *f = fopen("cmd", "rt");
    assert(f);
    fseek(f, 0, SEEK_END);
    long length = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *buffer = (char *)malloc(length + 1);
    buffer[length] = '\0';
    fread(buffer, 1, length, f);
    fclose(f);
    system("rm cmd");
    return buffer;
}
    
int main() {
    int x = check();
    
    if (x == 1)
        printf("There is a location like that");
    else
        printf("There isn't");
    
    return 0;
}

【问题讨论】:

  • 您应该使用getuid()getpwuid() 来查找用户名和主目录,而不是乱七八糟。
  • 您可以通过将终止符写入其最后一个元素来确保阻止溢出缓冲区的字符串终止:path[199] = 0;。但是 snprintf 函数始终存储终止空字符,必要时截断输出。” strncpy()strncat() 可能不会。
  • readFile("whoami") 的结果是什么?这似乎是一个可以使用调试器轻松解决的问题
  • @Shawn 谢谢你的建议!您能否提供说明其用法或文档的来源?
  • 你是说我吗?请参阅snprintf。哦,你编辑了请求!

标签: c c-strings


【解决方案1】:

whoami 命令打印出当前用户的名称,以换行符结束。将文件cmd 读回buffer 将包含相同的换行符。

path 字符串将是 "/home/USERNAME\n/.example"。试图执行 locate /home/USERNAME\n/.example 可能会混淆system 函数。

解决方案应该是去掉readFile 中的最后一个换行符。

【讨论】:

  • 太棒了!在添加包含主路径的字符串后,我通过使用 fflush(stdout) 解决了这个问题。感谢您的帮助。
【解决方案2】:

你的readFile函数有一些问题:

  • 名称具有误导性,应为 runCommand 或类似名称。

  • 您将命令输出附加到cmd 文件。如果在您运行程序之前存在cmd 文件,这将导致意外结果。你应该写:

    snprintf(cmd1, 99999, "%s > cmd", cmd);
    
  • 命令输出可能有一个尾随换行符。你应该修剪输出。

这是修改后的版本:

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

char *readFile(const char *cmd) {
    /* construct the command */
    size_t size = 4 + strlen(cmd) + 1;
    char *cmd1 = malloc(size);
    assert(cmd1);
    snprintf(cmd1, size, "%s > cmd", cmd);

    /* run the command */
    system(cmd1);
    free(cmd1);
        
    /* read the output file */
    FILE *f = fopen("cmd", "rt");
    assert(f);
    fseek(f, 0, SEEK_END);
    long length = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *buffer = malloc(length + 1);
    buffer[length] = '\0';
    fread(buffer, 1, length, f);
    fclose(f);
    unlink("cmd");

    /* trim trailing white space from the output */
    while (length > 0 && isspace((unsigned char)buffer[length - 1])) {
        buffer[--length] = '\0';
    }
    /* trim leading white space from the output */
    size_t i = 0;
    while (isspace((unsigned char)buffer[i]) {
        i++;
    }
    if (i > 0) {
        memmove(buffer, buffer + i, length - i + 1);
    }
    return buffer;
}

【讨论】:

    【解决方案3】:

    由于 Linux 为您的主目录 ("~") 提供了一个快捷方式,因此只需使用以下命令就可以避免整个 "whoami"/"getuid" 混乱:

    char *buffer = readFile("locate ~/.example");
    

    您仍然可能需要修剪尾随的换行符和/或 CR 字符,可能使用:

    strtok(buffer,"\r\n\t ");
    

    或者更好,正如@chqrlie 下面建议的那样。

    【讨论】:

    • Re “由于Linux有一个你的主目录的快捷方式(“~”),”:它是否依赖于shell(例如Bash)?
    • 这可能是正确的,但更通用的解决方案是使用在所有 shell 下定义的“$HOME”。
    • @SGeorgiades 完全正确。我只是 C 和 bash 的初学者,我仍然是,所以没有意识到这一点。为了做出贡献,除了返回的 $HOME 比主目录更多之外,至少在 ubuntu 中,我相信 getenv() 也是一种获取标准路径的跨平台方法。
    猜你喜欢
    • 2012-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-26
    • 2015-08-09
    • 2014-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多