1

I'm trying to find owners of files on the system that all start with a specific name, for example

[unu@here findtest]$ find . -ls
17295583    0 drwxrwxr-x   2 unu      unu            74 Mar 28 03:14 .
17295584    0 -rw-rw-r--   1 br_asd1  br_asd1         0 Mar 28 03:13 ./test1
17295585    0 -rw-rw-r--   1 br_asd2  br_asd2         0 Mar 28 03:13 ./test2
17295586    0 -rw-rw-r--   1 unu      unu             0 Mar 28 03:13 ./test33
17295587    0 -rw-rw-r--   1 br_bfg1  br_bfg1         0 Mar 28 03:14 ./test11
17295588    0 -rw-rw-r--   1 unu      unu             0 Mar 28 03:14 ./test22

I need find to only get files owned by users whose name starts with "br_".

So what I tried was

[unu@here findtest]$ find . -user "br_*" -ls
find: ‘br_*’ is not the name of a known user

Now a method I found that works is using awk, but I have certain problems with this method and it's not really usable for what I'm trying:

[unu@here findtest]$ find . -ls|awk '{if ($5 ~ "br_"||$6 ~ "br_") print $0}'
17295584    0 -rw-rw-r--   1 br_asd1  br_asd1         0 Mar 28 03:13 ./test1
17295585    0 -rw-rw-r--   1 br_asd2  br_asd2         0 Mar 28 03:13 ./test2
17295587    0 -rw-rw-r--   1 br_bfg1  br_bfg1         0 Mar 28 03:14 ./test11

Is there a way to add wildcards to the -user or -group options of find?

migrated from stackoverflow.com Mar 28 at 3:29

This question came from our site for professional and enthusiast programmers.

  • With getent passwd get a list of users which match br_.* and build a list like: --user br_asd1 --user br_asd2 --user br_bfg1 for find command. – Cyrus Mar 28 at 3:33
  • GNU find does not have options to pass wildcards for -user and -group fields. One way would be to use GNU coreutils stat and use the %U quantifier to get the owner name. Do something like for file in *; do [ -f "$file" ] || continue; owner=$(stat -c '%U' "$file"); if [[ "$owner" =~ ^br.* ]]; then echo "$file"; fi; done – Inian Mar 28 at 3:52
1

If you're writing for bash you can use an array to hold the set of users and another array to generate the corresponding syntax for find to match those users:

#!/bin/bash

# The beginning of the userids we need to match
match=br

# Find the matching set of users
users=($(
    getent passwd |
    awk -F: -vm="$match" 'BEGIN { re = "^" m } $1 ~ re {print $1}'
))

# Build the list of users ("find ( -user XX -o -user YY -o user ZZ ) ...")
finds=()
for user in "${users[@]}"
do
    finds+=('-o' '-user' "$user")
done
[[ ${#finds[@]} -gt 0 ]] && finds=('(' "${finds[@]:1}" ')')

# Execute the find command with the set of users
find . "${finds[@]}" -ls

As ever, you can prefix the find command with something like echo to see what would be executed. (Or you could run with bash -x to enable line-by-line debug reporting.)

  • Instead of the test in each iteration of that loop, you could just use "${finds[@]:1}" when calling find to ignore that first -o in the list. – Kusalananda Mar 28 at 8:46
0

If all you need is just a listing, I would go for a good old grep over find’s output. It would be slower, but much less typing.

At the very basic you might try this, for your example case:

find -printf '%u %p\n' | egrep '^br_'

This gives you the list of files owned by users br_*, shown with username and filename on each line.

You might then expand the output by fiddling the -printf format string.

For instance, to make it resemble more an ls output:

find -printf '%-8.8u %-8.8g %M %8s %t %p\n' | egrep '^br_'

The important thing to keep the command line as short as possible is to place the username at the very beginning, so that the egrep’s part can be kept short like that.

To see all that you can specify in the format string, go here, then search for -printf format.

Be careful though that if your files have newlines in their names then this solution might have problems in showing them.

Your Answer

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