确定路径是否是Node.js中另一个路径的子目录

jsd*_*rio 16 javascript regex node.js

我正在开发一个MQTT处理程序,我想为每个有一个事件监听器的父目录发出一个事件.例如:

如果有以下MQTT路径可用,那么有下标 - 这些路径的事件侦听器 -

  • test
  • replyer/request
  • test/replyer/request

有人发表主题test/replyer/request/@issuer,应该有2个事件被发现:test,test/replyer/request.

鉴于任何路径都是可能的并且没有可用的有效事件列表,我们必须仅检查路径是否是另一个路径的父路径.我们可以用正则表达式做到这一点吗?如果是这样,它会是什么样子?有更简单/更有效的解决方案吗?

Ily*_*kov 34

让Node本身完成工作.

const path = require('path');
const relative = path.relative(parent, dir);
return relative && !relative.startsWith('..') && !path.isAbsolute(relative);
Run Code Online (Sandbox Code Playgroud)

它也为你做了标准化.

const path = require('path');

const tests = [
  ['/foo', '/foo'],
  ['/foo', '/bar'],
  ['/foo', '/foobar'],
  ['/foo', '/foo/bar'],
  ['/foo', '/foo/../bar'],
  ['/foo', '/foo/./bar'],
  ['/bar/../foo', '/foo/bar'],
  ['/foo', './bar'],
  ['C:\\Foo', 'C:\\Foo\\Bar'],
  ['C:\\Foo', 'C:\\Bar'],
  ['C:\\Foo', 'D:\\Foo\\Bar'],
];

tests.forEach(([parent, dir]) => {
    const relative = path.relative(parent, dir);
    const isSubdir = relative && !relative.startsWith('..') && !path.isAbsolute(relative);
    console.log(`[${parent}, ${dir}] => ${isSubdir} (${relative})`);
});
Run Code Online (Sandbox Code Playgroud)

也适用于跨驱动器的Windows.

[/foo, /foo] => false ()
[/foo, /bar] => false (..\bar)
[/foo, /foobar] => false (..\foobar)
[/foo, /foo/bar] => true (bar)
[/foo, /foo/../bar] => false (..\bar)
[/foo, /foo/./bar] => true (bar)
[/bar/../foo, /foo/bar] => true (bar)
[/foo, ./bar] => false (..\Users\kozhevnikov\Desktop\bar)
[C:\Foo, C:\Foo\Bar] => true (Bar)
[C:\Foo, C:\Bar] => false (..\Bar)
[C:\Foo, D:\Foo\Bar] => false (D:\Foo\Bar)
Run Code Online (Sandbox Code Playgroud)

  • `relative.startsWith('..')` 是不负责任的。我可以按字面意思命名一个文件“......myfile”! (6认同)
  • 确实,这应该是真实且唯一的正确答案 (4认同)
  • 确实,.. bar中的..是名称的一部分。如果文件名中的换行符合法(尽管不受欢迎),我不明白为什么文件名不能以两个点开头。我认为那是您的实现失败的地方。 (3认同)
  • 我认为,重用Node.js所提供的内容比重新实现它(自己)(/sf/answers/2964909391/)更有意义。虽然,我有几点评论。它对于`['/ foo','/ foo / .. bar']`失败。而且我在path.isAbsolute中看不到任何含义。因此,我的版本为:`!! relative && relative.split(path.sep)[0]!='..'`。 (2认同)
  • @x-yuri _而且我在 `path.isAbsolute`_ 中没有看到任何含义,`path.win32.relative('C:\\Windows', 'D:\\Program Files') == 'D: \\程序文件'`。它不是以“..”开头,但它是绝对路径。 (2认同)
  • 什么?`/foo` *是* `/foo/..bar` 的父目录。`/foo/..bar` 解析为 `/foo/..bar`。`..bar` 是有效的文件名。在我的示例中,“..”和“bar”之间没有“/”。 (2认同)
  • 我无法理解为什么我们需要检查 `path.isAbsolute(relative)`。为什么 `path.relative` 的返回值会是绝对路径? (2认同)

Dom*_*ard 6

2017年末答案

在ES6中.

const isChildOf = (child, parent) => {
  if (child === parent) return false
  const parentTokens = parent.split('/').filter(i => i.length)
  return parentTokens.every((t, i) => child.split('/')[i] === t)
}
Run Code Online (Sandbox Code Playgroud)

如果您正在使用node.js并且想要使其跨平台,请包含该path模块并替换split('/')为split(path.sep).


这个怎么运作:

所以,你想知道一个目录(比如home/etc/subdirectory)是否是另一个目录的子目录(如home/etc).

它采用假设child和parent路径,并使用split以下方法将它们转换为数组:

['home', 'etc', 'subdirectory'], ['home', 'etc']
Run Code Online (Sandbox Code Playgroud)

然后迭代遍历parent数组中的所有标记,并child使用ES6 逐个检查它们在数组中的相对位置.every().

如果父母中的所有内容都符合孩子的一切,知道我们已经排除了他们是完全相同的目录(使用child !== parent),我们将得到答案.

  • 这在 Windows 和 UNC 路径上根本不起作用。Windows 允许为文件指定任何大小写,同时仍然可以找到任何文件。`Test` 与 `test` 相同。这对于像`home/this/../that`这样的相对目录也会失败,即使它们是完全有效的目录说明符。 (3认同)
  • 当涉及尾随分隔符时,这会失败:`isChildOf('a/b/c/d /','a/b/c /')=> false`.在进行拆分之前,您需要删除尾随路径分隔符. (2认同)
  • 或者在`parent.split('/')`和.every`之间添加`.filter(i => i.length)`.唉,这不是一个非常漂亮的班轮.无论如何编辑 (2认同)
  • **这不是一个安全的方法!!!** `isChildOf('/a/b/c/../../../etc/httpd/conf/httpd.conf', '/a/b /c')` 返回 `true`,但很容易让恶意用户获取敏感数据! (2认同)