相关文章推荐
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

What's the best way to check if two paths are equal in Bash? For example, given the directory structure

Desktop/ Downloads/ (symlink to ~/Downloads) Downloads/ photo.png

and assuming that the current directory is the home directory, all of the following would be equivalent:

./                    and ~
~/Desktop             and /home/you/Desktop
./Downloads           and ~/Desktop/Downloads
./Downloads/photo.png and ~/Downloads/photo.png

Is there a native Bash way to do this?

Most of the answers (except mine) will break with bind mounts and things other than symlinks that can make two paths refer to the same thing. – Peter Cordes Nov 29, 2015 at 8:05 Alternatively: stackoverflow.com/questions/284662/… Related: unix.stackexchange.com/questions/206973/… – Ciro Santilli OurBigBook.com Jun 9, 2018 at 19:31 re: @Ciro's links: How do you normalize a file path in Bash? will help you check for path equality like asked, but doesn't handle bind-mounts if you want to actually check if 2 paths are the same file/directory on disk. (Which is what the accepted answer here does.) How to find out whether two directories point to the same location has answers that do that, but the current accepted one is just path canonicalization. – Peter Cordes Jan 9, 2021 at 1:35 Nice! It looks like this method properly compares device and inode numbers as well, so I'm accepting this answer. – James Ko Nov 29, 2015 at 18:15 It helped me with windows git bashrc path logical operator comparision for cd-ing. Thanks. – ajay4q Feb 26, 2022 at 21:15

Another way is to use cd -P, which will follow symlinks but leave you in the physical directory:

cd -P "$dir1"
real1=$(pwd)
cd -P "$dir2"
real2=$(pwd)
# compare real1 with real2
                This only works for symlinks.  If the same filesystem is mounted at multiple points (e.g. mount --bind /raid0/var-cache /var/cache), then your best bet is to compare inodes.
– Peter Cordes
                Nov 29, 2015 at 7:37
                This doesn't do quite what you want, since the first directory-change affects the interpretation of any relative path in the second one. To fix this, you should move the cd inside the command substitution (so it runs inside the subshell).
– ruakh
                Nov 29, 2015 at 8:15
                @ruakh makes an excellent point that I'd overlooked. geirha's answer is also quite interesting.
– Tom Zych
                Nov 29, 2015 at 12:30

If you have coreutils 8.15 or later installed, you have a realpath command that fully normalizes a path. It removes any . and .. components, makes the path absolute, and resolves all symlinks. Thus:

if [ "$(realpath "$path1")" = "$(realpath "$path2")" ]; then
   echo "Same!"

Methods based on resolving symlinks fail when there are other factors involved. For example, bind mounts. (Like mount --bind /raid0/var-cache /var/cache

Using find -samefile is a good bet. That will compare filesystem and inode number.

-samefile is a GNU extension. Even on Linux, busybox find probably won't have it. GNU userspace and Linux kernel often go together, but you can have either without the other, and this question is only tagged Linux and bash.)

# params: two paths.  returns true if they both refer to the same file
samepath() {
    # test if find prints anything
    [[ -s "$(find -L "$1" -samefile "$2")" ]]  # as the last command inside the {}, its exit status is the function return value

e.g. on my system:

$ find /var/tmp/EXP/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb  -samefile /var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb 
/var/tmp/EXP/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb
$ stat {/var/tmp/EXP,/var}/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb
  File: ‘/var/tmp/EXP/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb’
Device: 97ch/2428d      Inode: 2147747863  Links: 1
  File: ‘/var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb’
Device: 97ch/2428d      Inode: 2147747863  Links: 1

You can use find -L for cases where you want to follow symlinks in the final path component:

$ ln -s   /var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb foo
$ find    /var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb  -samefile foo
$ find -L /var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb  -samefile foo
/var/cache/apt/archives/bash_4.3-14ubuntu1_amd64.deb

Obviously this works for paths which refer to directories or any type of file, not just regular files. They all have inode numbers.

usage:

$ samepath /var/cache/apt/  /var/tmp/EXP/cache/apt/  && echo true
$ ln -sf /var/cache/apt foobar
$ samepath foobar /var/tmp/EXP/cache/apt/  && echo true
samepath /var/tmp/EXP/cache/apt/ foobar   && echo true
samepath foobar   && echo true   # doesn't return true when find has an error, since the find output is empty.
find: `': No such file or directory

So find -L dereferences symlinks for -samefile, as well as for the list of paths. So either or both can be symlinks.

+1. Could you add some information about portability, though? (It doesn't look like -samefile is specified by POSIX?) – ruakh Nov 29, 2015 at 8:12 @ruakh: oh right, it's a GNU extension, probably only found in GNU find. Obviously bash doesn't come with GNU find, but I think the linux tag made me leave out a mention of this. – Peter Cordes Nov 29, 2015 at 8:17

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.

 
推荐文章