【问题标题】:Check if file system is case-insensitive in Python检查文件系统在 Python 中是否不区分大小写
【发布时间】:2011-12-13 18:40:17
【问题描述】:

如果文件系统不区分大小写,是否有一种简单的方法可以检查 Python?我特别考虑像 HFS+ (OSX) 和 NTFS (Windows) 这样的文件系统,您可以在其中访问与 foo、Foo 或 FOO 相同的文件,即使文件大小写被保留。

【问题讨论】:

    标签: python filesystems ntfs hfs+


    【解决方案1】:

    我认为我们可以在 Python 3.5+ 上使用pathlib 一行完成此操作,而无需创建临时文件:

    from pathlib import Path
    
    def is_case_insensitive(path) -> bool:
        return Path(str(Path.home()).upper()).exists()
    

    反之亦然:

    def is_case_sensitive(path) -> bool:
        return not Path(str(Path.home()).upper()).exists()
    

    【讨论】:

      【解决方案2】:

      关于不同文件系统等的好点,Eric Smith。但是为什么不使用带有 dir 参数的 tempfile.NamedTemporaryFile 并避免自己做所有的上下文管理器呢?

      def is_fs_case_sensitive(path):
          #
          # Force case with the prefix
          #
          with tempfile.NamedTemporaryFile(prefix='TmP',dir=path, delete=True) as tmp_file:
              return(not os.path.exists(tmp_file.name.lower()))
      

      我还应该提到,您的解决方案并不能保证您实际上是在测试是否区分大小写。除非您检查默认前缀(使用 tempfile.gettempprefix())以确保它包含小写字符。所以在这里包含前缀并不是可选的。

      您的解决方案会清理临时文件。我同意这似乎很明显,但一个人永远不知道,做一个吗?

      【讨论】:

        【解决方案3】:

        @Shrikant 答案的变体,适用于模块中(即不在 REPL 中),即使您的用户没有家:

        import os.path
        is_fs_case_insensitive = os.path.exists(__file__.upper()) and os.path.exists(__file__.lower())
        print(f"{is_fs_case_insensitive=}")
        

        输出(macOS):

        is_fs_case_insensitive=True ?
        

        还有 Linux 方面:

        (ssha)vagrant ~$python3.8 test.py
        is_fs_case_insensitive=False ?
        (ssha)vagrant ~$lsb_release -a
        No LSB modules are available.
        Distributor ID: Ubuntu
        Description:    Ubuntu 20.04 LTS
        Release:    20.04
        Codename:   focal
        

        FWIW,我通过以下方式检查了pathlibosos.path 的内容:

        [k for k in vars(pathlib).keys() if "case" in k.lower()]

        没有任何东西看起来像它,虽然它确实有一个pathlib.supports_symlinks,但没有区分大小写。

        【讨论】:

          【解决方案4】:

          我认为有一个更简单(而且可能更快)的解决方案。以下似乎适用于我测试的地方:

          import os.path
          home = os.path.expanduser('~')
          is_fs_case_insensitive = os.path.exists(home.upper()) and os.path.exists(home.lower())
          

          【讨论】:

          • 这适用于 macOS,但需要注意的是,如果以 no-login/no-shell 用户身份运行,您可能会遇到问题,有时出于安全原因,对于守护进程上的低权限用户(想想例如有人在用户nobody 下运行Django)。除此之外,这是一个不会与临时文件混淆的文件。
          【解决方案5】:

          我相信这是对这个问题最简单的解决方案:

          from fnmatch import fnmatch
          os_is_case_insensitive = fnmatch('A','a')
          

          发件人:https://docs.python.org/3.4/library/fnmatch.html

          如果操作系统不区分大小写,那么这两个参数都将 在比较之前被标准化为所有小写或大写 执行。

          【讨论】:

          • 不幸的是,这不能处理每个路径的不敏感性。只有@eric-smith 的答案似乎在这里有效。
          • 不。 OSX 不区分大小写并返回 False。
          【解决方案6】:

          Amber 提供的答案会留下临时文件碎片,除非明确处理关闭和删除。为了避免这种情况,我使用:

          import os
          import tempfile
          
          def is_fs_case_sensitive():
              #
              # Force case with the prefix
              #
              with tempfile.NamedTemporaryFile(prefix='TmP') as tmp_file:
                  return(not os.path.exists(tmp_file.name.lower()))
          

          虽然我的用例通常会不止一次地对此进行测试,所以我将结果存储起来以避免不得不多次接触文件系统。

          def is_fs_case_sensitive():
              if not hasattr(is_fs_case_sensitive, 'case_sensitive'):
                  with tempfile.NamedTemporaryFile(prefix='TmP') as tmp_file:
                      setattr(is_fs_case_sensitive,
                              'case_sensitive',
                              not os.path.exists(tmp_file.name.lower()))
              return(is_fs_case_sensitive.case_sensitive)
          

          如果只调用一次会稍微慢一点,而在其他情况下会明显更快。

          【讨论】:

          • 目前为止最好的解决方案,但是该函数应该将源目录作为输入参数,因为至少在 OSX 上,每个路径可能会有所不同。不是开玩笑。
          【解决方案7】:

          从 Amber 的回答开始,我想出了这段代码。我不确定它是否完全健壮,但它试图解决原始版本中的一些问题(我将在下面提到)。

          import os
          import sys
          import tempfile
          import contextlib
          
          
          def is_case_sensitive(path):
              with temp(path) as tmppath:
                  head, tail = os.path.split(tmppath)
                  testpath = os.path.join(head, tail.upper())
                  return not os.path.exists(testpath)
          
          
          @contextlib.contextmanager
          def temp(path):
              tmphandle, tmppath = tempfile.mkstemp(dir=path)
              os.close(tmphandle)
              try:
                  yield tmppath
              finally:
                  os.unlink(tmppath)
          
          
          if __name__ == '__main__':
              path = os.path.abspath(sys.argv[1])
              print(path)
              print('Case sensitive: ' + str(is_case_sensitive(path)))
          

          没有在mkstemp中指定dir参数,区分大小写的问题就模糊了。您正在测试临时目录所在位置是否区分大小写,但您可能想了解特定路径。

          如果您将从mkstemp 返回的完整路径转换为大写,您可能会错过路径中某处的转换。例如,我在 Linux 上使用 vfat 在/media/FLASH 安装了一个 USB 闪存驱动器。测试/MEDIA/FLASH 下是否存在任何东西总是会失败,因为/media 位于(区分大小写的)ext4 分区上,但闪存驱动器本身不区分大小写。挂载的网络共享可能是这样的另一种情况。

          最后,也许在 Amber 的回答中不言而喻,您需要清理 mkstemp 创建的临时文件。

          【讨论】:

            【解决方案8】:
            import os
            
            if os.path.normcase('A') == os.path.normcase('a'):
                # case insensitive
            else:
                # case sensitive
            

            【讨论】:

            • 至少在 Mac OS 上是错误的。文件系统不区分大小写,normcase 返回 2 个不同的结果
            • 那么这会是 Python 的错误吗?
            【解决方案9】:
            import os
            import tempfile
            
            # By default mkstemp() creates a file with
            # a name that begins with 'tmp' (lowercase)
            tmphandle, tmppath = tempfile.mkstemp()
            if os.path.exists(tmppath.upper()):
                # Case insensitive.
            else:
                # Case sensitive.
            

            【讨论】:

            • 如果 tmppath 恰好都是大写字母会怎样?规范保证不会发生这种情况吗?
            • @LorinHochstein - 如代码中的 cmets 所述,mkstemp() 的“前缀”参数的默认值为 "tmp"(小写)。 docs.python.org/library/tempfile.html#tempfile.mkstemp 因此生成的文件名总是以 3 个小写字符开头。
            • @Lorin Hochstein:文档说默认前缀是'tmp',您可以检查gettempprefix() == gettempprefix.lower() 或在mkstemp() 中明确设置prefix
            • 如果临时文件不在感兴趣的文件系统中会怎样?
            • 之后别忘了删除文件! os.path.remove(tmppath)
            猜你喜欢
            • 2010-11-20
            • 1970-01-01
            • 1970-01-01
            • 2012-03-24
            • 1970-01-01
            • 2021-12-03
            • 2013-06-21
            • 2015-06-04
            • 2010-09-30
            相关资源
            最近更新 更多