0

I'm trying to achieve the same format of listing that I get from ls -l when using find. Albeit with the full path name at the end rather than the filename. FWIW I'm using find (GNU findutils) 4.5.11.

I've found the -ls flag, which is nice, but it spaces the columns much more than ls -l, which is not useful, especially with a full path name at the end, as it is very likely to push the results on to two lines and makes it difficult to read them. For example...

$ find /example -ls 7439 4 -rwxrwxr-x 1 example example 1579 Oct 26 1985 /path/to/first/file/that/was/found 530 4 -rwxrwxr-x 1 example example 122 Oct 26 1985 /path/to/second/file/that/was/found 

So while that does the job, I want to do better. Looking at the -printf flag for find, it seems the same thing can be achieved. So far I have -printf '%M %u %g %p\n' which is great, and I can customise which info I want and add the others in there. But...

The problem is, how can I align the columns?

When the user and group names are a different length, this doesn't work so well. For example...

$ find /example -printf '%M %u %g %p\n' -rwxr-xr-x root root /path/to/first/example/file -rwxr-xr-x example example /path/to/second/example/file 

How can I solve this? Preferably using only find, but piping to something else would be ok if it's not possible. The man page for find doesn't seem to have any option for this using -printf.

2
  • You could pipe the find output into column -t but if there are spaces in the filenames, the result will be wrong Commented Jul 25, 2019 at 21:56
  • That's awesome, thanks, especially because it's very easy to use, keeping the spaces in filenames caveat in mind. In my case that should rarely be an issue. Perhaps put this as an answer? Commented Jul 25, 2019 at 22:14

2 Answers 2

4

The format specifiers of -printf in GNU find takes width and alignment (etc.) qualifiers, just as the printf() C function, meaning you can align the data left or right and, crucially for your current project, allocate a certain width for the data.

Default output:

$ find . -printf '%M %u %g %p\n' drwxr-xr-x kk wheel . -rw-r--r-- kk wheel ./.zshrc -rw-r--r-- kk wheel ./file drwxr-xr-x root wheel ./rootstuff -rw-r--r-- root wheel ./rootstuff/SECRET -rw------- kk wheel ./.viminfo 

Specifying widths (6) for user and group columns (right-justified):

$ find . -printf '%M %6u %6g %p\n' drwxr-xr-x kk wheel . -rw-r--r-- kk wheel ./.zshrc -rw-r--r-- kk wheel ./file drwxr-xr-x root wheel ./rootstuff -rw-r--r-- root wheel ./rootstuff/SECRET -rw------- kk wheel ./.viminfo 

Same, but left-justified:

$ find . -printf '%M %-6u %-6g %p\n' drwxr-xr-x kk wheel . -rw-r--r-- kk wheel ./.zshrc -rw-r--r-- kk wheel ./file drwxr-xr-x root wheel ./rootstuff -rw-r--r-- root wheel ./rootstuff/SECRET -rw------- kk wheel ./.viminfo 

With file sizes in bytes, allocating 5 digits (zero-filled, because just showing how it could be done):

$ find . -printf '%M %-6u %-6g %05s %p\n' drwxr-xr-x kk wheel 00512 . -rw-r--r-- kk wheel 00000 ./.zshrc -rw-r--r-- kk wheel 00095 ./file drwxr-xr-x root wheel 00512 ./rootstuff -rw-r--r-- root wheel 00000 ./rootstuff/SECRET -rw------- kk wheel 00922 ./.viminfo 

Note that the ls command may well allocate widths for its columns dynamically based on the actual need whereas your find -printf command would have to use static widths, unless you run find twice to first compute the needed space for each column and then again to actually format the output (this is why the find -ls output is so wide, it does not use a two-pass approach and instead just gives each column ample space in the hope that everything will align somewhat nicely).


Of course, if you don't have many thousands of files, and you just want a listing of everything, recursively (i.e., you don't need to filter or do something else with the found pathnames in find), you could just use ls:

$ ls -d -l **/* -rw------- 1 kk wheel 922 Jul 25 23:46 .viminfo -rw-r--r-- 1 kk wheel 0 Jul 19 23:39 .zshrc -rw-r--r-- 1 kk wheel 95 Jul 25 23:51 file drwxr-xr-x 2 root wheel 512 Jul 26 00:00 rootstuff -rw-r--r-- 1 root wheel 0 Jul 26 00:00 rootstuff/SECRET 

This relies on the ** globbing pattern being available, which it is by default in the zsh shell, and in bash if you use shopt -s globstar. It also relies on the shell matching hidden names. The zsh shell will do this if you set the GLOB_DOTS option with setopt GLOB_DOTS, and bash does it with shopt -s dotglob.

This would not work if your globbing pattern expands to a list so long that it generates an "Argument list too long" error.

This output would be different from the ordinary ls -l -R output in that directories would not be listed individually with breaks between them.

1
  • Perfect, thanks, this is exactly what I was looking and hoping for. Appreciate the clear and detailed explanation. Can cope without the dynamic column widths. Commented Jul 25, 2019 at 22:29
0

Here is another simple alternative

$ find example -exec ls -ld {} + 
2
  • Yes, this would work too, but note that find may invoke ls several times if you do it this way and that the columns in the output may have different widths each time. One of the issues in this question was that the columns needed to be aligned. Commented Nov 14, 2021 at 9:20
  • True. But the original issue was about readability. So a change in alignment now and then shouldn't be a problem. But that point is worth mentioning. Commented Nov 14, 2021 at 12:39

You must log in to answer this question.