0

I need to generate a list of all files and directories with their permission and owner e.g.

-rw-rw-r--    black www-data     foo/
-rw-r--r--    black www-data     foo/foo.txt
-rw-rw-r--    black www-data     bar/
-rwxrwxr-x    black www-data     bar/foo.sh

I need this list to compare it with another instance where I have a bug.

Kusalananda
  • 333,661
Black
  • 2,079
  • Just for sure. You do not use selinux in the another instance. – K-attila- Mar 03 '23 at 13:35
  • 1
    if both directories are on the same system, you can use diff's -r option to do a recursive comparison of files in both directories - e.g. diff -u -r /path/to/dir1 /path/to/dir2. This will show files/subdirs that only exist in one of the two directories, as well as the differences between files with the same name, if any. – cas Mar 03 '23 at 13:38

5 Answers5

1

In order to be independent of the sometimes dynamically changing output format of ls, I would recommend using stat with the --printf option to have control over the outputs format. The following would create such a list in a more "machine-readable" format, using the | symbol as separator (the choice is arbitrary, see last paragraph for more on this):

stat --printf="%A|%U|%G|%n\n" /path/to/dir/* > list_of_tiles.psv

This would output the list of files and properties in the form

-rw-rw-r--|black|www-data|foo.txt
drw-rw-r--|black|www-data|bar/

However, this would by default not list the files and directories inside the sub-directories. To achieve that, there are several ways:

  • If you are using bash, you can enable the globstar option to allow recursively-acting wildcard patterns:

    shopt -s globstar
    stat --printf="%A|%U|%G|%n\n" /path/to/dir/** > list_of_tiles.psv
    

    with output

    drwxr-xr-x|mygroup|myuser|subdir1
    -rw-r--r--|mygroup|myuser|subdir1/subfile1.txt
    -rw-r--r--|mygroup|myuser|subdir1/subfile2.txt
    drwxr-xr-x|mygroup|myuser|subdir2
    -rw-r--r--|mygroup|myuser|subdir2/subfile3.txt
    -rw-r--r--|mygroup|myuser|that_file.txt
    -rw-r--r--|mygroup|myuser|this_file.txt
    
  • Otherwise, you may need to resort to GNU find:

    find . -printf '%M|%g|%u|%p\n'
    

    with output

    drwxr-xr-x|mygroup|myuser|.
    drwxr-xr-x|mygroup|myuser|./subdir1
    -rw-r--r--|mygroup|myuser|./subdir1/subfile1.txt
    -rw-r--r--|mygroup|myuser|./subdir1/subfile2.txt
    drwxr-xr-x|mygroup|myuser|./subdir2
    -rw-r--r--|mygroup|myuser|./subdir2/subfile3.txt
    -rw-r--r--|mygroup|myuser|./that_file.txt
    -rw-r--r--|mygroup|myuser|./this_file.txt
    
  • If neither is available, this is a combination of both:

    find . -exec stat --printf="%A|%U|%G|%n\n" {} \;
    

Also note that this approach will only really be successful for your purpose of (automated?) comparisons if the filenames themselves don't contain the separator character | or new-lines (which are unfortunately also allowed characters). This is one of the reasons why it is discouraged to parse the output of ls or find via scripts. The same holds true for any choice of the separator character; this answer uses | simply because it is less frequently found in filenames than, e.g., the space.

AdminBee
  • 22,803
1

To get that information for a single file or directory on Linux, you may use the stat utility with a custom output format:

$ stat -c '%A %U %G %n' ~/.bashrc
-rw-r--r-- myself myself /home/myself/.bashrc

Would you want tab-delimited output, use $'%A\t%U\t%G\t%n' as the argument to the -c option (in shells that understand $'...' as a C-string).

To get the information for everything in the current directory and below, recursively, use find:

find . -exec stat -c $'%A\t%U\t%G\t%n' {} +

On BSD systems (any of the free ones, and macOS), you will want to use stat -f '%Sp %Su %Sg %N' instead (each S indicates "symbolic" output rather than numeric). There, you may also use %t to insert tabs:

find . -exec stat -f '%Sp%t%Su%t%Sg%t%N' {} +

Example run on macOS:

$ tree
.
├── dir1
│   └── file
└── dir2
    └── othername

3 directories, 2 files

$ find . -exec stat -f '%Sp%t%Su%t%Sg%t%N' {} +
drwxr-xr-x  myself  staff   .
drwxr-xr-x  myself  staff   ./dir2
-rw-r--r--  myself  staff   ./dir2/othername
drwxr-xr-x  myself  staff   ./dir1
-rw-r--r--  myself  staff   ./dir1/file
Kusalananda
  • 333,661
1

If you have GNU find, you can use its printf action:

find . -printf "%M %u %g %p\n"

This will list all files in the current directory and any subdirectories, with their type and permissions in ls style, owner and group, and full name starting from the current directory.

If you want consistent spacing, you can use field width specifiers, e.g.

find . -printf "%M %-20u %-20g %p\n"

You can output tabs with \t.

Stephen Kitt
  • 434,908
0

I think you need to redirect the results to a log file. Try using this:

ls -la /route/of/dir >> /route/ofyouneed/the/log/log.txt
AdminBee
  • 22,803
0

The zsh shell has both array comparison operators and a stat builtin to retrieve file metadata (since 1996 so predates both GNU and BSD stat which are from the early 2000s).

There you can do:

zmodload zsh/stat
details=()
for f ($dir/**/*(DN)) stat -LsH s -- $f && details+="$s[mode] $s[uid] $s[gid] $f:t"

Once you have those results for two separate directories in two arrays (like $details1 and $details2, you can do:

print -rC1 'Only in first:' ${details1:|details2}
print -rC1 'Only in second:' ${details2:|details1}
print -rC1 'In both:' ${details1:*details2}