3

In a bash script I have the following lines:

TARGETS="$(findmnt -n -v -t btrfs -o TARGET --list)"
UUIDS="$(findmnt -n -v -t btrfs -o UUID --list)"

I run this script on some servers (running Arch Linux). However, it fails on NFS servers. In that case, findmnt returns multiple mount points for a given UUID (due to bind mounts on the NFS servers). A requirement of my script is that I end up with a list of one mount point for each UUID.

I found a really good answer here (Showing only “interesting” mount points / filtering non interesting types) and tried all the suggestions discussed there and I still cannot eliminate findmnt from returning multiple mount points for a given UUID -- even when the problem is narrowed to duplicates caused by bind mounts only. (Did I miss something?)

As a temporary workaround, what I have done is make a limited tab file that contains only the mounts I want to have show up, and then I use the -F option with findmnt. My custom tab file is a copy of fstab with all the bind mounts deleted.

TARGETS="$(findmnt -n -v -t btrfs -o TARGET --list -F /etc/fstab.custom)"
UUIDS="$(findmnt -n -v -t btrfs -o UUID --list -F /etc/fstab.custom)"

Obviously, this is a brittle approach that will break as soon as anything changes in /etc/fstab and my custom tab file isn't manually updated. (I also had to manually delete a few non-bind-mounts that cause duplicates from my custom file, so it definitely isn't a robust solution at this stage.)

What is a better way to achieve the desired result? I can envision that this might be solved with findmnt filters or it might be solved with external filters applied to the list returned by findmnt.

All my bind mounts start with "/srv/nfs/". Filtering those out of the list would be a good start, although that also strikes me as a somewhat brittle solution (because someone might decide to change how NFS bind mounts are organized on the server one day).

Also I'm not good enough at bash or awk to know the right way to post-process the results returned by findmnt. I thought about this:

findmnt -n -t btrfs -o UUID,TARGET --list | grep -v -E "/srv/nfs|.tapp|/var/log"

But that seems just as brittle as the custom tab file because any changes to path names will break it.

Here's an example of what is returned by findmnt -n -t btrfs -o UUID,TARGET --list on one of my systems (mount points simplified):

473c1g34-23ec-41dd-671f-c71473ad7d36 /
473c1g34-23ec-41dd-671f-c71473ad7d36 /srv/nfs/foo/wunder
473c1g34-23ec-41dd-671f-c71473ad7d36 /srv/nfs/foo/tinder
473c1g34-23ec-41dd-671f-c71473ad7d36 /.tapp
473c1g34-23ec-41dd-671f-c71473ad7d36 /var/log
473c1g34-23ec-41dd-671f-c71473ad7d36 /var/log/.tapp
13f4361e-771e-44b3-7413-a613bf4e331b /bar
13f4361e-771e-44b3-7413-a613bf4e331b /srv/nfs/bar/user1/Documents
13f4361e-771e-44b3-7413-a613bf4e331b /srv/nfs/bar/user6/Desktop
13f4361e-771e-44b3-7413-a613bf4e331b /srv/nfs/bar/user3/Documents
13f4361e-771e-44b3-7413-a613bf4e331b /srv/nfs/bar/user4/Documents
13f4361e-771e-44b3-7413-a613bf4e331b /srv/nfs/bar/user5/Documents
13f4361e-771e-44b3-7413-a613bf4e331b /srv/nfs/bar/user6/Documents
13f4361e-771e-44b3-7413-a613bf4e331b /bar/.tapp
3ac663cc-e7e4-4764-7a7a-1d3fa16b376b /foo/files
3ac663cc-e7e4-4764-7a7a-1d3fa16b376b /srv/nfs/foo/files
4c61646e-1a1d-4d67-7d47-1446e41d6341 /bar/baz
4c61646e-1a1d-4d67-7d47-1446e41d6341 /srv/nfs/bar/baz/goop/stuff
4c61646e-1a1d-4d67-7d47-1446e41d6341 /srv/nfs/bar/baz/goop/widgets
4c61646e-1a1d-4d67-7d47-1446e41d6341 /srv/nfs/bar/baz/pie
4c61646e-1a1d-4d67-7d47-1446e41d6341 /srv/nfs/bar/baz/goop/gadgets
4c61646e-1a1d-4d67-7d47-1446e41d6341 /bar/baz/.tapp

I would need to filter out all nfs bind mounts (those starting with /srv/nfs) and all mounts points containing /.tapp and the mount point for /var/log. Is there a way to do that with findmnt options? If not, how to do it in bash?

UPDATE: to keep the question focused, I would be happy with a way to exclude just the bind mounts from the findmnt results list. In order to provide an answer, please feel free to ignore the other duplicate mount points such as that created by /var/log.

UPDATE 2: here's an example of the findmnt output in json format. I don't see a way to distinguish the bind mounts from the one in this output that I would want to keep (/foo/files).

{"target": "/srv/nfs/bar/user1/Documents", "uuid": "13f4361e-771e-44b3-7413-a613bf4e331b"},
{"target": "/srv/nfs/home/user5/Desktop", "uuid": "13f4361e-771e-44b3-7413-a613bf4e331b"},
{"target": "/srv/nfs/home/user3/Documents", "uuid": "13f4361e-771e-44b3-7413-a613bf4e331b"},
{"target": "/srv/nfs/home/user4/Documents", "uuid": "13f4361e-771e-44b3-7413-a613bf4e331b"},
{"target": "/foo/files", "uuid": "3ac663cc-e7e4-4764-7a7a-1d3fa16b376b"},
2

From List only bind mounts, it seems that bind mounts cannot be distinguished from the original after mounting. While partial binds (where a subdirectory of a mount point is bind mounted somewhere) do show up differently in findmnt output, there's no distinguishing a mountpoint that was bind mounted elsewhere.

So if I did:

sudo mount -o bind / /srv
sudo mount -o bind /media /mnt

I wouldn't be able to distinguish between / and /srv in terms of mount information, but I might be able to distinguish between /media and /mnt.

So, what remains is simply removing duplicated entries from findmnt:

findmnt -n -t btrfs -o UUID,TARGET -r | awk '!a[$1]++'

Use the -r option instead of -l, which will escape special characters in the output so that you can safely handle paths:

$ findmnt -n -t ext4 -o UUID,TARGET -r
d4873b63-0956-42a7-9dcf-bd64e495a9ff /
..
d4873b63-0956-42a7-9dcf-bd64e495a9ff /srv/media
d4873b63-0956-42a7-9dcf-bd64e495a9ff /a\x20b
d4873b63-0956-42a7-9dcf-bd64e495a9ff /srv/a\x20b

Here, a b is escaped as a\x20b. You can use printf "%b" in bash to expand the escaped characters:

$ findmnt -n -t ext4 -o UUID,TARGET -r | bash -c 'while read -r uuid point; do printf "%b\n" "$point"; done'
/
...
/srv/media
/srv/a b
/a b
/srv/a b
  • Yes, my findmnt supports json output. However, in my output I see bind mounts as targets at the same level as mounts such as 3ac663cc-e7e4-4764-7a7a-1d3fa16b376b /foo/files. Examining the json, I see no way to distinguish between them on this system. I will post the json output in an update to my question, but they are all listed exactly the same. – MountainX Mar 14 '18 at 1:03
  • @MountainX what version of util-linux? – muru Mar 14 '18 at 1:07
  • @MountainX alternately, I think you could use lsblk for the same purpose, perhaps? lsblk --json -o UUID,fstype,mountpoint and then use jq to filter only btrfs filesystems? – muru Mar 14 '18 at 1:23
  • @MountainX nah, I tried it out. lsblk reports a bind mountpoint instead of the original. I'll keep looking for something that matches binds. – muru Mar 14 '18 at 1:35
  • @MountainX I rewrote the answer. Seems it's not possible to distinguish bind mounts. – muru Mar 14 '18 at 2:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.