如何在 Windows 上获取 Node.js 中文件的大小写精确路径?

How to obtain case-exact path of a file in Node.js on Windows?

我有一条路径,比方说 C:\temp\something.js,我想在 Windows 上获得路径的大小写版本 - 所以如果 C:\Temp\someThing.js 存储在磁盘上,我想得到这个值(路径)。

在Node.js中如何从前面的路径得到后面的路径?

我已经通过 FS API (https://nodejs.org/api/fs.html) 但我没有发现任何有用的东西 (即 fs.realpathSync, fs.statSync, fs.accessSync不是 return 我需要的)。

具有不区分大小写的文件系统(Windows、macOS)的平台使得很难获得给定的 case-exact 形式,可能是 case-variant 路径- 似乎没有 system 的 API,因此 Node.js(或 Python、Perl、...)等环境不应受到指责。

更新@barsh was nice enough to package 将下面的代码与 npm 一起使用,因此 您可以使用
轻松安装它 npm install true-case-path

glob npm package 及其 nocase 选项在这里派上用场(尽管它需要对 Windows 进行一些调整);基本上,将输入路径视为一个 glob - 即使它是一个 文字 路径 - 使 glob() return 成为存储在文件系统中的真实情况:

  • 在项目文件夹中安装包 globnpm install glob(添加 --save--save-dev根据需要)。

  • 使用下面的trueCasePathSync()函数;请参阅有关使用和限制的评论;值得注意的是,虽然输入路径也被规范化,但 .. 开头的路径 受支持,因为 path.normalize() 不支持t 相对于当前目录解析它们。

    • 注意trueCasePathSync() 不是 return 规范路径:如果您传入相对路径,你也会得到一个相对的输出路径,并且没有符号链接被解析。如果您想要规范路径,请将 fs.realPathSync() 应用于结果。
  • 应该在 Windows、macOS 和 Linux 上工作(尽管在区分大小写的文件系统上的用处有限),经过测试Node.js v4.1.1

    • 注意:在 Windows 上,没有 尝试对驱动器号或 UNC 共享组件进行大小写更正路径(服务器名称、共享名称)。
/*
SYNOPSIS
  trueCasePathSync(<fileSystemPath>)
DESCRIPTION
  Given a possibly case-variant version of an existing filesystem path, returns
  the case-exact, normalized version as stored in the filesystem.
  Note: If the input path is a globbing *pattern* as defined by the 'glob' npm
        package (see prerequisites below), only the 1st match, if any,
        is returned.
        Only a literal input path guarantees an unambiguous result.
  If no matching path exists, undefined is returned.
  On case-SENSITIVE filesystems, a match will also be found, but if case
  variations of a given path exist, it is undefined which match is returned.
PLATFORMS
    Windows, OSX, and Linux (though note the limitations with case-insensitive
    filesystems).
LIMITATIONS
  - Paths starting with './' are acceptable, but paths starting with '../'
    are not - when in doubt, resolve with fs.realPathSync() first.
    An initial '.' and *interior* '..' instances are normalized, but a relative
    input path still results in a relative output path. If you want to ensure
    an absolute output path, apply fs.realPathSync() to the result.
  - On Windows, no attempt is made to case-correct the drive letter or UNC-share
    component of the path.
  - Unicode support:
    - Be sure to use UTF8 source-code files (with a BOM on Windows)
    - On OSX, the input path is automatically converted to NFD Unicode form
      to match how the filesystem stores names, but note that the result will
      invariably be NFD too (which makes no difference for ASCII-characters-only
      names).
PREREQUISITES
  npm install glob    # see https://www.npmjs.com/search?q=glob
EXAMPLES
  trueCasePathSync('/users/guest') // OSX: -> '/Users/Guest'
  trueCasePathSync('c:\users\all users') // Windows: -> 'c:\Users\All Users'
*/
function trueCasePathSync(fsPath) {

  var glob = require('glob')
  var path = require('path')

  // Normalize the path so as to resolve . and .. components.
  // !! As of Node v4.1.1, a path starting with ../ is NOT resolved relative
  // !! to the current dir, and glob.sync() below then fails.
  // !! When in doubt, resolve with fs.realPathSync() *beforehand*.
  var fsPathNormalized = path.normalize(fsPath)

  // OSX: HFS+ stores filenames in NFD (decomposed normal form) Unicode format,
  // so we must ensure that the input path is in that format first.
  if (process.platform === 'darwin') fsPathNormalized = fsPathNormalized.normalize('NFD')

  // !! Windows: Curiously, the drive component mustn't be part of a glob,
  // !! otherwise glob.sync() will invariably match nothing.
  // !! Thus, we remove the drive component and instead pass it in as the 'cwd' 
  // !! (working dir.) property below.
  var pathRoot = path.parse(fsPathNormalized).root
  var noDrivePath = fsPathNormalized.slice(Math.max(pathRoot.length - 1, 0))

  // Perform case-insensitive globbing (on Windows, relative to the drive / 
  // network share) and return the 1st match, if any.
  // Fortunately, glob() with nocase case-corrects the input even if it is 
  // a *literal* path.
  return glob.sync(noDrivePath, { nocase: true, cwd: pathRoot })[0]
}

我认为唯一的办法就是遍历父目录中的文件,然后找到匹配的文件。下面提供了不区分大小写系统的示例实现。

如果文件系统不区分大小写,则同一文件夹中不能有两个以小写字母共享相同名称的文件。以下实现利用了这一点。

/**
 * @param {string} filePath
 * @returns {string|undefined}
 */
function getRealPath(filePath) {
    /** @type {number} */
    var i;
    /** @type {string} */
    var dirname = path.dirname(filePath);
    /** @type {string} */
    var lowerFileName = path.basename(filePath).toLowerCase();
    /** @type {Array.<string>} */
    var fileNames = fs.readdirSync(dirname);

    for (i = 0; i < fileNames.length; i += 1) {
        if (fileNames[i].toLowerCase() === lowerFileName) {
            return path.join(dirname, fileNames[i]);
        }
    }
}

如果您的用例需要处理区分大小写的文件系统,我建议保留一个列表 潜在匹配,然后打开潜在匹配查看内容,以确定好的。

从节点 9 开始,fs.realpathSync.native 似乎可以解决问题。