如何编写一个正则表达式来匹配文件路径?

3
我已经使用这个命令大约一个到两个小时了,我担心自己可能失去了客观性。目标是仅匹配给定给Bash的相对文件路径。
第一个相对路径是"."或"./some/file/path"。
第二个相对路径是".."或"../some/file/path"。
其中"/some/file/path"的长度是任意的。
我一直在使用grep和bash尝试弄清楚如何将其实现到我的脚本中,以便我可以扩展它到它的绝对文件路径,以便"./some/file/path"或"../some/file/path"变成"/the/absolute/file/path";我已经搞清楚了。
我的问题是匹配相对路径。
我一直在使用的代码是:
echo "../some/file/path" | egrep '\.{1}/?[[:graph:]]?+$'

并且

echo "../some/file/path" | egrep '\.{2}/?[[:graph:]]?+$'

我已经缩小了我的问题范围,发现问题与IT技术有关。

echo ".." | egrep '\.{2}'

只要出现了2 + n次,点号就会匹配,不一定是恰好2次。当我将其更改为

echo ".." | egrep '\.{1}' 

出于某些我无法想象的原因,此处仍然会匹配成功。

最终的实现应该像这样工作:

 41 _expand_relative_path () {
 42         if [[ "$1" =~ ^\.{1}/?[[:graph:]]?+$ ]]; then
 43                 echo "."
 44         elif [[ "$1" =~ ^\.{2}/?[[:graph:]]?+$ ]]; then
 45                 echo ".."
 46         else    
 47                 echo "$1"
 48         fi
 49 }

根据我的教材,定量符号{n}只匹配前一个元素出现 n 次。但实际上它匹配的是 n 或更多次!我做错了什么?

1
不以“/”开头的路径是相对路径。 - heemayl
你是在说 '^[^/].?.?/?[[:graph:]]+$' 是更简单的表达式。有没有办法用我的表达式准确地识别出 . 元素出现 n 次? - user3159377
你为什么需要这个?不,相对路径只是 [^/].* - n. m.
4个回答

5
正则表达式用于匹配相对路径,不能以斜杠开头:
^[^/].*

对我不起作用。请查看链接。只有第一行中的机器,没有相对路径。 我已经在https://i.imgur.com/XXI6Osq.png发布了截图。 - Farrukh Waheed
@FarrukhWaheed 这个正则表达式匹配所有相对路径。你想匹配哪个路径?请键入它,不要发布它的图像链接。 - Bohemian

1
使用^\.{1}/?[[:graph:]]+$的问题是/被指定为可选项,接下来的[[:graph:]]字符类匹配任何可见的内容,包括更多的句点。此外,您使用了?+量化字符类,这意味着“零次或一次,占有性”:它不需要匹配,但如果它匹配了,就不会“放弃”匹配的内容,以便让模式的其余部分尝试成功-这可能不是您想要的。

当您说echo ".." | egrep '\.{2}'时,您的意思是“字符串中某个位置有两个连续的句点”-但这并不意味着它不能有更多的句点或其他任何内容,除非使用^$锚点,这将限制为仅且仅两个句点。

正如其他人所指出的,任何不以/开头的路径都是相对路径,因此^[^/].*有效。但是,如果您想查找在带有其他文本的文本文件中的相对路径,则可能会有用:

(\.{1,2}(?:\/[[:alnum:]]*)*)

正则表达式演示 输出:

enter image description here


我注意到你使用了pcre,我以前没有见过这个冒号。那是做什么的?例如第二个子表达式中的 (?:\/[[ - user3159377
这些是仅用于分组的括号 (?:pattern),它们不进行捕获。如果要使用这些,您可能需要使用 pcregrep 而不是 egrep... - Scott Weaver
我之前认为括号( )是用来分组的? **我指的是子表达式中,出现在匹配元素零次或一次的量词符?之后的冒号:**。 - user3159377
(Pattern) 会在匹配结果中捕获 Pattern 并用于分组,但 (?:Pattern) 只是分组而不捕获任何内容。 - Scott Weaver
实际上这里外面的括号是不必要的。 - Scott Weaver

1
echo ".." | egrep '\.{2}'

will match the dot as long as it has 2 + n occurrences, not exactly 2 occurrences as expected.

默认情况下,grep会打印包含模式的行。任何包含两个以上连续点的行必定包含两个连续点,因此该模式匹配。

The same thing happens when I change it to

echo ".." | egrep '\.{1}' 

will still match for some reason I can't figure out.

同样的事情:字符串“..”包含一个“。”,因此它与模式匹配。

现在考虑您原始的模式,'\.{2}/?[[:graph:]]?+$'

  1. 首先,注意它没有锚定到字符串的开头,因此它将匹配形式为/foo/bar../baz(和其他路径)。您需要在模式中使用初始的^来锚定它。
  2. 通过使用?量词,可以使前导点后面的/变成可选项。如果您的目标是特别匹配第一个段是..的路径,则不清楚为什么要这样做。我唯一能想到的是,您希望匹配恰好是..本身的路径,而您的模式确实可以做到这一点,但接受的范围太大了。
  3. 下一个段是[[:graph:]]?+,这似乎是写更标准的[[:graph:]]*的奇怪方式。此外,您似乎在这里依赖于[[:graph:]]将匹配/字符的事实,它确实会匹配,因此您可能会将前面的可选/直接卷入字符类中:'^\.{2}[[:graph:]]*$'
  4. 现在请注意,[[:graph:]]还匹配.。这就解释了为什么原始模式匹配包含两个以上连续点的字符串:前两个由\.{2}匹配,没有任何东西被/?匹配,剩余的点(和可能的其他字符)由[[:graph:]]?+匹配。
  5. 最后,请考虑\.\.\.{2}更短、更清晰,尤其是纯粹的\.\.{1}明显得多。
当然,在他的回答中,@Bohemian提供了匹配每个可能的相对路径的自然模式,但如果您想要一个特定的模式来匹配第一个片段为...的路径,包括那些没有其他片段且没有尾随/的路径,则可以尝试使用以下模式:
egrep '^\.{1,2}(/.*[^/])?$'
  • 它在开头(^)和结尾($)锚定,因此只执行整行匹配。
  • 匹配的行必须以一个或两个点(\.{1,2})开头
  • 其他任何内容都是可选的((...)?),但如果存在该可选段,则必须以/开头并以不是/的字符结尾。之间可以是任意数量的任何字符(包括零个)(.*)。
  • 请注意,Unix文件和目录名称可以包含空格和非图形字符,因此在原始模式中使用[:graph:]会将其限制为可能路径的子集。

我非常喜欢你将我的表达式分解并解释每个步骤发生的方式。你的假设是正确的。尽管如此,这是我的错,我忽略了./../也是允许的,这就解释了为什么我将其作为可选项。 - user3159377

0

对于Windows系统:^.*\\(?!.*\\)(.*)$

或者对于Linux系统:^.*/(?!.*/)(.*)$

或者两者都适用:

^.*(?:\\|/)(?!.*(?:\\|/))(.*)$

它匹配 filename.extension 在一个 .../path/filename.extension 或者 ...\path\filename.extension,因为它检查最后出现的 \ 或者 / 并且从那个点开始捕获每个字符。


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