正则表达式查找匹配文件扩展名的文件,除非文件名包含字符串

Regex to find files matching file extension except if filename contains string

我在 nginx 中为特定文件启用了缓存,如下所示:

location ~* \.(?:css|js)$ {
access_log off;
add_header Cache-Control "no-transform,public,max-age=31536000,s-max-age=31536000";
expires 1y;
}   

我想在这里做的是排除所有匹配模式 i18n-*.js 的文件,结果缓存所有 .js 文件,除了以 i18n 开头的文件。

我尝试进行否定查找以排除模式,但由于非捕获组,它无法正常工作:

location ~* \.(?!i18n-.*\.js)(?:css|js)$ {
        access_log off;
        add_header Cache-Control "no-transform,public,max-age=31536000,s-max-age=31536000";
        expires 1y;
}

这里的智能解决方案是什么?我不是正则表达式专家,所以简短的解释也会有所帮助。

官方文档describes位置树的遍历方式:

Rregular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.

在此基础上配置如下:

location ~* \.(i18n-.*\.js)$ {
  access_log off;
  expires off;
}

location ~* \.(css|js)$ {
  access_log off;
  expires 1y;
  add_header Cache-Control public;
}  

注意:正则表达式中的问号是多余的,除非用作变量 docs:

A named regular expression capture can be used later as a variable:

server {
  server_name   ~^(www\.)?(?<domain>.+)$;

  location / {
    root   /sites/$domain;
  }
}

如果使用 ?: 语法跳过捕获组,则需要稍后使用它们,否则您可以删除以简化位置语法。

我相信 Anatoly 的回答是您问题的完整解决方案。我只是想提供比评论允许的更多见解。

你的正则表达式做得很好。一个很好的组合问题,你的表情非常接近。

这就是它不起作用的原因

.               # matches any character except newline
(?!i18n-.*\.js) # A negative lookahead which actually does what you intended it to do
(?:css|js)$`    # extension list
  1. 在你的每场比赛中,. 恰好与此处的字面句号相匹配。在没有锚点或断言的情况下,允许从这里开始。 (demo)。如果没有量词,所有尝试都会产生错误的结果。
  2. 第一个句点后没有量词,因此无论如何,它都无法正确获取您的完整文件名。前瞻评估而不消耗。
    1. a(?=1) 将匹配 aa1 但不会匹配 a2.
    2. a(?=1)c 将因 a1c.
    3. 而失败
    4. a(?=1)1ca(?=1)\dca(?=1).c 等将匹配 a1ca1c.
  3. 在这种情况下,您的 .* 需要 之后。因为前瞻看起来超出了到目前为止所捕获的内容。
    1. 在这里暂停一下,再看一眼 demo 可能会让您了解它在做什么。
    2. 如您所见,它在第一行的第一个字符处意识到匹配将失败,因此它会继续下一个字符。
  4. 没有断言(例如^)或参考点字符(例如\/),这就是那种情况下发生的情况。添加这样的内容将使您的表达有效。
    1. 发生了非常相似的事情here,它意识到第一个字符不匹配,因此重新开始搜索。它知道搜索要求,在我们的例子中,它从行的开头开始,所以它开始寻找下一个换行符。

值得注意的是,完全为了将来参考,如果你想使用参考点字符,如 \/,你将使用这样的表达式 \/(?!i18n-[^\/]*\.js)[^\/]*(?:css|js)$,否则路径包含的斜杠可能会产生意想不到的结果。

你拥有所有元素,但是,正如你所说 I'm no regex expert, so a brief explanation would be helpful, too.