什么是 Windows 下等同于 pwd.getpwnam(username).pw_dir 的命令?

8
Python的pwd模块提供了访问getpwnam(3)POSIX API的功能,可以通过用户名获取特定用户的主目录,并确定该用户名是否有效。如果使用不存在的用户名调用pwd.getpwnam,则会引发异常。
乍一看,似乎可以通过os.path.expanduser('~username')以跨平台方式实现相同的结果。然而,在Windows XP上的Python 2.6中,即使对于不存在的用户名,它似乎也不会产生故障。此外,在Windows XP上的Python 2.5上,它似乎甚至对于有效用户也无法正常工作。
在Windows上如何可靠地获取此信息?

1
总是喜欢一个好的“Python on Windows”的问题。如果可能的话,我们尽早为该平台带来启蒙,这对所有人都更好。 - Matt Joiner
“~user” 不应该在2.5版本上工作,它是在2.6及以上版本中实现的,但以一种有问题的方式 - 请查看下面我的答案以获取可工作的实现。是的,我不得不偷看注册表。 - Nas Banov
4个回答

4

阅读2.6文档,显示os.path.expanduser()在Windows上存在问题:

在Windows上,如果设置了HOME和USERPROFILE,则使用它们;否则,将使用HOMEPATH和HOMEDRIVE的组合。一个初始的~user通过从上述创建的用户路径中删除最后一个目录组件来处理。

这是什么意思?这意味着所有用户主目录都必须在同一个父目录下。不行!

虽然有点难找,但这里有一个解决方案,可以按给定名称查找本地用户:

from win32security import LookupAccountName, ConvertSidToStringSid
from _winreg import OpenKey, QueryValueEx, HKEY_LOCAL_MACHINE

def getUserDir(userName):
    ssid = ConvertSidToStringSid(LookupAccountName(None, userName)[0])
    key = OpenKey(HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\\' + ssid)
    return QueryValueEx(key, 'ProfileImagePath')[0]

1
这只会产生本地用户的结果吗? - akira
1
这个getUserDir看起来很有前途。我在我的问题上试用了它,但不幸的是,我立即遇到了一个问题,我的代码正在作为服务运行,显然没有映射到用户(不知何故),因此其中一个调用失败了(抱怨缺少这样的映射的异常)。目前我停在那里,但我可能会在某个时候重新考虑这个问题。也许一旦我设置所需的任何映射,这个函数就能满足要求。 - Jean-Paul Calderone
1
@Jean-Paul:啊,这似乎完全是另一个问题。默认情况下,win服务在内置安全主体LocalService下运行,这是一种用户,但可能有一些限制(没有NetBEUI,没有GUI)。可选择将服务运行在另一个特定的用户登录下。例如,PostgreSQL将其守护程序运行在用户名“ postgres”下。 - Nas Banov
pwd.getpwnam 的最接近等效物将是 win32net.NetUserGetInfo,使用级别 4 的信息。结果包括用户的 home_dir 和漫游 profile 目录。如果它们都为空,它还包括 user_sid 来查找本地配置文件目录。 - Eryk Sun
问题在于系统有单独的“个人资料”和“主目录”概念,对当前用户访问的分别是 %USERPROFILE%%HOMEDRIVE%%HOMEPATH%。更大的问题是已知用户文件夹(例如“桌面”)可以单独设置为任何本地或远程目录,而不是在配置文件目录中的默认值。确定这需要使用 winreg.LoadKey (备份/恢复特权)加载用户的配置文件注册表窗口(“ntuser.dat”),并从用户的“Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders”读取值。 - Eryk Sun

3
我是Windows安全方面的新手...但是阅读MSDN和一些博客后,我觉得微软希望我们处理其他用户特定数据的方式是通过获取用户令牌来完成。
曾经有一个很好的维基页面:Keith Brown的.Net开发人员指南Windows安全性...你仍然可以在Google缓存中找到它,搜索“pluralsight keith.guidebook”。
情况1:如果您没有用户密码: 对于本地账户,您可以尝试像Nas Banov建议的那样读取Windows注册表,并且在SO或互联网上还有其他一些方法。
我不确定各种Windows版本对于新创建的用户的行为方式...那些从未执行过交互式会话登录的用户...是否会自动创建他们的注册表、主目录和配置文件数据? 我在Windows XP上进行了一些测试,在创建本地账户后这些注册表键不存在...但在这种情况下,您可以根据所有用户注册表值猜测它们的位置...或者只是失败:)
对于桌面应用程序,当应用程序作为已登录用户运行时,我使用类似于以下内容来获取主目录....并使用CSIDL_APPDATA来获取等效的~/.local,用于漫游配置文件,或者只是CSIDL_LOCAL_APPDATA。
from win32com.shell import shell, shellcon
# See microsoft references for further CSIDL constants
# http://msdn.microsoft.com/en-us/library/bb762181(VS.85).aspx
folder_name = shell.SHGetFolderPath(0, shellcon.CSIDL_PROFILE, 0, 0)

阅读Keith Brown的文章“如何为用户获取令牌”..您可以寻找其他获取用户令牌的方法,而无需密码...

情况2:如果您有用户密码:

阅读MSDN,我得出的印象是,如果我有一个用户令牌,我可以通过调用以下代码之类的东西来获取其文件夹...但对我没有起作用。(不确定原因)

token = win32security.LogonUser(
            username,
            None, # we uses UPN format for username
            password,
            win32security.LOGON32_LOGON_NETWORK,
            win32security.LOGON32_PROVIDER_DEFAULT,
            )
folder_name = shell.SHGetFolderPath(0, shellcon.CSIDL_PROFILE, token, 0)

这就是我最终得到的代码...由于需要用户名和密码,它远非完美。
token = win32security.LogonUser(
            username,
            None, # Here should be the domain ... or just go with default values
            password,
            win32security.LOGON32_LOGON_NETWORK,
            win32security.LOGON32_PROVIDER_DEFAULT,
            )
win32security.ImpersonateLoggedOnUser(token)
folder_name = shell.SHGetFolderPath(0, shellcon.CSIDL_PROFILE, 0, 0)
win32security.RevertToSelf()

这个问题与编程有关:如何使用Python找到真实的用户主目录?


0

你可以选择使用win32api.GetUserName()(仅限当前用户)或者win32net.NetUserGetInfo()(包括本地主机以及任何服务器上的任意用户)。后者可能会慢一些,因为从操作系统获取这些信息可能需要一些时间。

  import win32net

  def userDir(username):
        return win32net.NetUserGetInfo(None, username, 1).get("home_dir")

或者你可以在Windows上扩展环境变量USERPROFILE,或在Unix上扩展HOME来获取有关当前登录用户的信息:

  def userDir():
      if os.platform.system() == 'Windows':
          return os.environ['USERPROFILE']
      elif os.platform.system() == 'Linux':
          return os.environ['HOME'] 
      else:
          return None

不起作用 - 这将仅返回当前用户的信息。问题是关于任何“特定”的用户。 - Nas Banov
@Akira:听起来不错,但是怎么做呢?win32net.NetUserGetInfo(None, userName, 4))返回'home_dir': u'',也就是无用的。 - Nas Banov
@Nas Banov:是的,很有趣,不是Windows7的问题吗? - akira
@Akira:我怀疑问题与Win7无关,因为我在XP上测试过了。可能需要阅读一下详细的文档以获取更多信息。 - Nas Banov
NetUserGetInfo只能检索域中可用的信息...在许多默认安装中,主文件夹并非由域配置,而是由本地配置。为了获取当前用户的主文件夹,我正在使用SHGetFolderPath或SHGetKnownFolderPath...主要缺点是需要用户名和密码。 - Adi Roiban
显示剩余2条评论

0

这似乎只适用于当前用户,但在我的(winxp)机器上,os.path.expanduser('〜')返回我的主目录。


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