检查两个路径是否相同,即使它们的字符串不完全匹配

检查两个路径是否相同,即使它们的字符串不完全匹配

我有一个脚本依赖于一些环境变量,其中一些是路径

$(pwd)/expected-subdir该脚本通过检查以下项来检查它是否在正确的目录中运行:$VARIABLE_PATH/expected_subdir

当然,如果$VARIABLE_PATH == $(pwd)/(我在输入变量时自然地在变量末尾附加了一个斜杠),即使目录在技术上是正常的,字符串匹配也会失败。

有没有办法唯一地标识一个目录,给出它的路径,并查看另一条路径是否会将您路由到同一位置?给出的路径可能不合理,因此仍应进行此检查,但似乎没有必要强制路径完全匹配,因为一个斜杠甚至一个相对目录(我知道这些有时可能很糟糕)会将您路由到同一位置。

答案1

其他答案提及stat。让我们改进一下方法:

a="$(stat --format=%d:%i -- "$(pwd)/expected-subdir/")"         || printf "Error A\n" >&2
b="$(stat --format=%d:%i -- "$VARIABLE_PATH/expected-subdir/")" || printf "Error B\n" >&2
[ "$a" = "$b" ] || printf "Not the same path.\n" >&2

笔记:

  • 我们正在使用stat --format=,因此不需要进一步解析。
  • %d是设备号,%i是 inode 号。仅后者是不够的,因为不同设备(文件系统)上的两个对象可能具有相同的 inode 号。
  • shell 足够智能,可以分别处理 内部和外部的引号$( )
  • printf-s 只是错误处理的存根。在实际脚本中,你可能需要类似

    [ "$a" = "$b" ] || { printf "Not the same path.\n" >&2; exit 2; }
    
  • 不存在的路径将会引发stat错误。

  • 尾部斜杠很重要。如果foo是目录的符号链接,stat foo将检查符号链接,同时stat foo/将检查目录。或者研究stat -L并使用它。
  • --以...开头$(pwd)$VARIABLE_PATH以...开头-(参见准则 10这里)。

readlink -f(另一个答案中也提到了)和realpath实用程序。其中一种方法可能比 更好stat。这取决于路径的哪些方面对您来说很重要。主要区别:

  1. 如果/foo/bar/是绑定挂载,/moo/baz/那么stat --format=%d:%i会告诉你它们是相同的,但是readlink或者realpath会认为它们是不同的。
  2. 符号链接和 的情况就变得复杂了..。参见man 1 realpath,尤其是-L-P选项。

对于符号链接,如果你的脚本将目录向上更改(朝向/,例如cd ..),你可能会根据起始位置获得不同的结果,即使statreadlink -f表示两个起点相同(比较为什么ls ..当我位于符号链接目录中时会显示真实的父内容?)请考虑这种方法:

# early in the script
set -P
cd .   # seems like no-op but updates the PWD variable to physical path

涉及绑定挂载时,如果您的脚本更改了目录,您可能会根据起始位置获得不同的结果,即使stat两个起始点相同。考虑带有和的示例/foo/bar//moo/baz/上面已经介绍过)。显然/foo/可能与完全不同/moo/。但也/foo/bar/abc可能不同于/moo/baz/abc因为任何abc可能是对其他东西的独立绑定挂载。因此,不仅cd ..可能将您置于不同的位置,而且cd abc

嗯,abc可能是一个文件。如果你将另一个文件绑定到/moo/baz/abc但没有绑定到会怎么样/foo/bar/abc?即使stat你在同一个地方,感知到的文件也会有所不同!

由于这些问题,您可能确实更喜欢readlinkrealpath超过stat


statreadlink并且realpath不是 POSIX 所要求的。对于你的情况,可移植的解决方案可能如下所示:

a="$(cd -P -- "expected-subdir" && pwd -P)"                || exit 1
b="$(cd -P -- "$VARIABLE_PATH/expected-subdir" && pwd -P)" || exit 1
[ "$a" = "$b" ] || { printf "Not the same path.\n" >&2; exit 2; }

它的工作原理是cd-ing 到任一路径并使用 检索它pwd。这些操作明确强制以“物理”方式运行(-P)。

set -P也不是 POSIX。如果您希望 POSIX shell“模拟” set -P,请将cd和替换为pwd

cd()  { command cd  -P "$@"; }
pwd() { command pwd -P "$@"; }

POSIX 要求最后一个-P-L选项才能生效,因此pwd -L即使函数“注入” ,仍然会检索逻辑路径-P; 也是一样cd -L

答案2

您可以比较它们的 inode 编号,可从以下位置获取ls -i

ls -di path/to/dir

-d防止ls显示目录内容的 inode id);

或来自stat

stat path/to/dir | grep -o 'Inode:\ [0-9]*' | cut -d' ' -f2

您还可以使用它readlink -f来获取目录的完整路径。

相关内容