在Windows上,如何在Node.js中获取文件的完整路径?

17

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

如何在Node.js中从前一个路径获取后一个路径?

我已经查阅了FS API (https://nodejs.org/api/fs.html),但是没有发现有用的内容(即fs.realpathSyncfs.statSyncfs.accessSync没有返回我需要的内容)。


https://github.com/nodejs/node/issues/3352 - MartyIX
类似问题 https://dev59.com/Jm445IYBdhLWcg3wpL1_ for .NET - MartyIX
另一种解决方案:https://github.com/Microsoft/TypeScript/issues/3626#issuecomment-148966821,作者是duanyao(https://github.com/duanyao)。 - MartyIX
3个回答

16

大小写不敏感的文件系统平台(如Windows、macOS)使得获取给定路径的确切大小写形式变得异常困难,即使路径大小写可能有所不同 - 系统似乎没有相应的API,因此Node.js(或Python、Perl等环境)并不是罪魁祸首。

更新@barsh很好心地将下面的代码打包起来供npm使用,因此您可以轻松安装它
npm install true-case-path

glob npm包及其nocase选项在这里拯救了我们(尽管在Windows上需要进行一些调整);基本上,将输入路径视为通配符 - 即使它是一个字面路径 - 使glob()返回存储在文件系统中的确切大小写:

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

  • 使用下面的trueCasePathSync()函数; 请参阅注释以了解用法和限制; 特别地,尽管输入路径也被规范化,但以..开头的路径不受支持,因为path.normalize()不会相对于当前目录解析这些路径.

    • 注意: trueCasePathSync()不会返回规范路径:如果你传入一个相对路径,那么你也会得到一个相对输出路径,并且没有符号链接被解析。如果你想要规范路径,请将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]
}

2
谢谢!我已将此添加到npm作为“true-case-path”。https://www.npmjs.com/package/true-case-path - barsh
很遗憾,至少在2017年12月29日之前,这对许多情况都不起作用。它不能处理驱动器盘符。也不能处理UNC路径。 - gman
@gman:正如源代码注释中所述,它确实无法处理驱动器字母和UNC共享路径的部分(服务器名称、共享名称)。但是,我已经将这些信息添加到上面的要点中,以使其更加明显。如果您知道其他限制,请告诉我们。 - mklement0

13

在Node 9及以后版本中,fs.realpathSync.native似乎可以解决问题。


是的。这在我的情况下解决了问题。我在Windows上使用__dirname助手时遇到了问题,我使用了const dirname = fileSystem.realpathSync.native(__dirname),现在我得到了__dirname的正确区分大小写的值。 - Ionel Lupu
这是截止2022年最佳的答案。 - IVO GELOV
然而,这也具有将符号链接解析为其目标的附加效果,因此它不能严格用于纠正任何给定路径的大小写。 - Erik J

2
我认为唯一的方法是循环遍历父目录中的文件,然后找到匹配的文件。下面提供了一个适用于不区分大小写系统的示例实现。
如果文件系统不区分大小写,则同一文件夹中不能有两个名称相同但小写字母不同的文件。以下实现利用了这一点。
/**
 * @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]);
        }
    }
}

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

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