在Linux中如何获取C/C++的用户名?

45

如何在程序中不使用环境变量 (getenv, ...) 来获取实际的“用户名”?该程序是基于C/C++和Linux操作系统。


1
C和C++函数之间有什么区别?你是指类方法吗? - AlexTheo
8
POSIX API中的所有函数都是用C编写的,并且可以在C++中调用。我不理解你的限制。 - drrlvn
3
这个限制没有任何意义...如果你需要与POSIX操作系统交互,你必须使用C(或汇编)进行编程。你如何定义“C++函数”?C++标准库没有提供任何相关内容,即使有也只是getlogin_r的包装器而已。 - Matteo Italia
1
@Armed9Gagger:我非常确定他不是指你所说的那样,因为那没有任何意义...你能告诉我们他在任务中写的确切话吗?可能他只是想让你为getlogin_r编写一个C++封装(使用std::string等),而不是C风格函数(使用char *等)。 - Matteo Italia
2
@Armed9Gagger:从技术上讲,cstdlib等是C++的头文件(在std命名空间中提供遗留的C库)。但是,POSIX头文件不包含在标准C中,它们是操作系统API,旨在从多种语言(主要是C和C++)中使用。 - Matteo Italia
显示剩余8条评论
7个回答

66

getlogin_r()函数在unistd.h中定义,返回用户名。有关更多信息,请参见man getlogin_r

其签名为:

int getlogin_r(char *buf, size_t bufsize);

毋庸置疑,这个函数同样可以在C或C++中调用。


7
@Armed9Gagger,C++库中没有API可以定义POSIX中的这些内容。C++应该使用POSIX函数。 - drrlvn
12
注意,getlogin_r函数返回进程控制终端登录的用户的名称。这可能与作业运行的用户不同。如果未连接到控制终端(例如,进程已守护化),此函数也将失败。 - Doug Richardson
1
getlogin() 这个更简单 - rohitsakala
3
需要TTY存在-如果您通过某种调度程序来派遣应用程序,则可能会失败。请参考Nemanja Boric的答案以获得更“健壮”的版本。 - ben.pere

60

来自http://www.unix.com/programming/21041-getting-username-c-program-unix.html

/* whoami.c */
#define _PROGRAM_NAME "whoami"
#include <stdlib.h>
#include <pwd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
  register struct passwd *pw;
  register uid_t uid;
  int c;

  uid = geteuid ();
  pw = getpwuid (uid);
  if (pw)
    {
      puts (pw->pw_name);
      exit (EXIT_SUCCESS);
    }
  fprintf (stderr,"%s: cannot find username for UID %u\n",
       _PROGRAM_NAME, (unsigned) uid);
  exit (EXIT_FAILURE);

}

只需将主要代码放入类中进行封装:

class Env{
    public:
    static std::string getUserName()
    {
        uid_t uid = geteuid ();
        struct passwd *pw = getpwuid (uid);
        if (pw)
        {
            return std::string(pw->pw_name);
        }
        return {};
    }
};

仅适用于C语言:

const char *getUserName()
{
  uid_t uid = geteuid();
  struct passwd *pw = getpwuid(uid);
  if (pw)
  {
    return pw->pw_name;
  }

  return "";
}

5
很可能你不能使用C 标准库,而不是所有C函数。你需要直接或间接地使用C函数来执行你的任务。 - Kos
严谨地说,你的程序不是缺少了一个endpwent()调用吗? - SO Stinks
@NemanjaBoric 你不能只使用argv[0]来获取程序名称吗?即使在编译后它被更改了。 - The XGood
@TheXGood 啊,你是指针对“_PROGRAM_NAME”。是的,这个例子只是我贴的链接中的示例。 - Nemanja Boric
1
@Dr.PersonPersonII,“你的程序不是缺少了一次endpwent()调用吗?” - 那是在一个或多个调用getpwent()之后使用的,而当前版本和早期版本的回复都没有使用,据我所知,所以不需要。 - Nick

4
#include <iostream>
#include <unistd.h>
int main()
{
    std::string Username = getlogin();
    std::cout << Username << std::endl;
    return 0 ;
}

另一种方法是这样的 -

#include <iostream>
using namespace std;
int main()
{
       cout << system("whoami");
}

2

1
cuserid()函数已经从POSIX标准中删除(参见https://pubs.opengroup.org/onlinepubs/9699919799/functions/getlogin.html#tag_16_203_08):原先在POSIX.1-1988标准中定义的`cuserid()`函数提供的信息可以通过以下方式获取:`getpwuid(geteuid())`。 - Andrew Henle

1

正在逐渐研究现代C++规范

static auto whoAmI = [](){ struct passwd *tmp = getpwuid (geteuid ());
  return tmp ? tmp->pw_name : "onlyGodKnows"; 
}

2
这应该使用 lambda 而不是普通函数/类,有什么原因吗? - Ankur S
在我编写代码的上下文中,本地函数是适当的隐藏方式。在我看来,如果没有其他要求,通过函数(如您所建议的)/lambda可以避免手动创建类,它们(除其他外)本质上就是由编译器构建的可执行对象。 - juanba

0
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>

// Return username or nullptr if unknown.
const char* username() {
    const passwd* pw = getpwuid(geteuid());
    return pw == nullptr ? nullptr : pw->pw_name;
}

-2
今天我必须做同样的事情,但不想包含任何特定于操作系统的头文件。因此,以下是您可以跨平台执行而无需使用任何Linux / Windows特定头文件的方法:
#include <stdio.h> 
#include <memory>
#include <stdexcept>
#include <array>
#include <regex>

std::string execute_command(std::string cmd) 
{
    std::array<char, 128> buffer;
    std::string result;
    
    #if defined(_WIN32)
        #define POPEN _popen
        #define PCLOSE _pclose
    #elif defined(unix) || defined(__unix__) || defined(__unix)
        #define POPEN popen
        #define PCLOSE pclose
    #endif

    std::unique_ptr<FILE, decltype(&PCLOSE)> pipe(POPEN(cmd.c_str(), "r"), PCLOSE);
    if (!pipe) 
    {
        throw std::runtime_error("popen() failed!");
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) 
    {
        result += buffer.data();
    }
    return result;
}
std::string get_current_username()
{
    #if defined(_WIN32)
        // whoami works on windows as well but it returns the name 
        // in the format of `computer_name\user_name` so instead
        // we use %USERNAME% which gives us the exact username.
        #define USERNAME_QUERY "echo %USERNAME%" 
    #elif defined(unix) || defined(__unix__) || defined(__unix)
        #define USERNAME_QUERY "whoami"
    #endif
    auto username = execute_command(USERNAME_QUERY);
    // this line removes the white spaces (such as newline, etc)
    // from the username.
    username = std::regex_replace(username, std::regex("\\s"), "");
    return username;
    
}

这在Linux和Windows上都可以正常工作,并且兼容C++11!

在线测试:https://paiza.io/projects/e/xmBuf3rD7MhYca02v5V2dw?theme=twilight


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接