Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

On Linux, is there a way to sort ls output by users? What I try to achieve is something like this:

user_a   file1
user_a   file2
user_b   another_file
user_c   this_file
user_c   that_file
user_d   file3

I am aware that a listing like this would also contain file size, permissions etc. – my main concern is the sorting by users. Would be quite handy, wouln't it

So far I found ls -l | sort -k 3 to sort by column three which [if using ls -l] contains the file owner [thus sort -k 4 to sort by group].

BUT what if the file owner isn't in row three? Is there another way to achieve this, independent from the number of the column?


Update: I forgot to mention that I work in BASH and try to stick to it for quite a while from here on so things don't become more complicated.

share|improve this question
1  
You can write a C program. Walk over the list of files (specified as arguments, or all in the current directory), then use fstat() to get the st_uid and use that to lookup the name of the owner with getpwuid() and group the file info with that. No ls, no columns -> no problem with column numbers. –  Anthon Apr 18 '13 at 17:20
    
BUT what if the file owner isn't in row three? where else it can be? –  Rany Albeg Wein Apr 19 '13 at 16:40
    
@nylon100: a lot of unixes (and even on the same one, different version of the OS or the ls command) will act differently with many commands. It's good to be extra careful about portability, as a simple script here would be dangerous there (ex: if you rely on column 3 to find which files to delete, and it happens it contains something else than the owner's name, you may end up deleting every files you wanted to keep...) –  Olivier Dulac Apr 19 '13 at 17:16
    
@OlivierDulac I agree with the principle, but I've yet to see an example of an ls that doesn't follow the standard column ordering. And incorrect sorting shouldn't result in much damage even in edge cases unless other best practices are also broken. I think it's also worth noting that the OP specifically indicates they are using Linux. Sure, they could have a non-GNU (and non-BSD) ls installed. But they could also unknowingly have a rootkit installed that makes ls output an ASCII-art animation of Rick Astley. I think that'll break your awk-based parsing as well. :-D –  depquid Apr 19 '13 at 19:32
    
@RanyAlbegWein @OlivierDulac on Linux ls -ls lists the file's size first and the file owner in the fourth row [at least it does here] - and yes, the file size is also presented in sixth row then which makes the -s option redundant, but as this is the only possibility I came across so far it brought me to the idea that there might also be other scenarios where this is possible. –  erch Apr 20 '13 at 8:17

6 Answers 6

up vote 3 down vote accepted

Determining which column the owner name is in from a single ls -l output without knowing which is which is not possible. You could try to match the entries in each column with the passwd file, but there is no guarantee that you would not be matching the group column or the filename column both which could only contain names you find in /etc/passwd.

If you want to go with ls, you could run the program twice, once as ls -l and once as ls -g. The latter drops the owner so by matching lines based on the other information you would be able to determine the owner name without specification. This is however not an exercise I would be happy to do in a bash shell script.

share|improve this answer
    
'Matching with the passwd file sounds great – but how to achieve this? –  erch Apr 18 '13 at 17:41
1  
@cellar.dweller if that would work I would do it in my programming language of choice, but with some output you can never be sure which column are the owner names and not filenames or groupnames. That makes the effort futile IMHO. Better start with the uid info as I indicated in my comment on your question. –  Anthon Apr 18 '13 at 17:45
    
There might come a time when I would be able to code in C but for the moment I am lucky to get along witch basic commands and options, but thanks :) –  erch Apr 18 '13 at 17:52
    
Most of the C library functions are also available in in some form in Python, Ruby or Perl. Those are somewhat more forgiving than C, to do these kind of things. –  Anthon Apr 18 '13 at 18:01
    
Forgiving is nice for the Pro's who might have overseen something [oopsie!] but did a good job for 99% of what they did OR ended up giving their best trying to give 99%. But then I welcome 'not forgiving' because if IT is based on math there should be 'right' or 'wrong' and as little as possible in between. I accept strict rules as long as they are there to work as properly as one is able to do. Everything else is confusing - especially for Newbs. It might just be me... –  erch Apr 19 '13 at 0:08

Don't parse ls: use stat

stat -c "%U %n" -- * | sort
share|improve this answer
2  
Note that there are several implementations of stat. That one is the GNU one. –  Stéphane Chazelas Apr 18 '13 at 18:59
    
OS X's stat uses -f instead of -c and it only supports %u (UID) and not %U (username). –  ؘؘؘ Apr 19 '13 at 14:52
1  
@LauriRanta stat -f "%Su %N" -- * | sort should work on OS X and *BSD. –  depquid Apr 19 '13 at 15:40
    
@Glenn Why is stat better in this case? In my tests, it still doesn't handle newlines in filenames well. –  depquid Apr 19 '13 at 15:41
    
newlines in filenames is really a pathological edge case: no line-oriented tools will be able to handle that. stat is not really safer here, but it does provide the (apparently) desired output. Also, since it's inherently difficult to parse the filename from ls -l output, I'm showing a better technique. Stephane's answer demonstrates a safe way using the zero-byte as a delimiter. –  glenn jackman Apr 19 '13 at 17:26

With zsh, you can define sort orders and use it in globbing qualifiers like:

zmodload zsh/stat
uid() zstat -A REPLY +uid -- $REPLY

... *(no+uid)

(n for numerical order, o for order, +uid to order with the uid function). The idea is to have a function that takes a filename in $REPLY and returns something in $REPLY that zsh sorts on.

So, for instance with GNU ls:

ls -ldU -- *(no+uid)

With GNU only tools, the equivalent would be something like:

find . ! -name . -prune -printf '%U\t%p\0' |
  sort -zn |
  tr '\0\n' '\n\0' |
  cut -f2- |
  tr '\0\n' '\n\0' |
  xargs -r0 ls -ldU
share|improve this answer
    
I forgot to mention that I work in BASH [and updated my question]. I try to stick to it for quite a while from here on so things don't become more complicated. [I love the choices I have with Linux and don't want to be ungrateful, but for a newb all this possibilities can become to much.] –  erch Apr 18 '13 at 17:37

Since the OP doesn't stipulate particular portability requirements (other than for use in Bash), and since parsing ls seems to remain the popular approach, and since the stat-based solution doesn't seem to handle newlines in filenames any better (who puts newlines in filenames, anyway?), I'm going to throw in my own suggestion for the most elegant solution:

I believe the OP actually had almost the best answer. It just needs to be escaped to prevent unexpected behavior from aliasing (remember, this is a Bash-specific solution):

\ls -l | sort -k 3

18 characters, only requires ls and sort, and no loops. Elegant, easy to understand, and reliable.

Also, as Olivier pointed out in his answer, it may be desirable to limit sort to only the third column, rather than the whole line starting with that column:

\ls -l | sort -k 3,3

I will retract this answer if someone can find me an implementation of ls -l that doesn't contain the owner in the third column or a way to break this solution that doesn't break solutions given in other answers.

share|improve this answer

1) Determine which column is the name:

myls='ls -al'
echo '+' > /tmp/MYOWNFILE.$$  #so file will be of size 2, "+" and newline.
zeuser=$( $myls /tmp/MYOWNFILE.$$ | awk -v myname=$(whoami) '{ for (field=1;field<=NF;field++) { if ($field == myname) { print field ; break } } }' )
zesize=$( $myls /tmp/MYOWNFILE.$$ | awk '{ for (field=1;field<=NF;field++) { if ($field == 2) { print field ; break } } }' )
zename=$( $myls /tmp/MYOWNFILE.$$ | awk -v filename=/tmp/MYOWNFILE.$$ '{ for (field=1;field<=NF;field++) { if ($field == filename) { print field ; break } } }' )
rm /tmp/MYOWNFILE.$$

It put in variable zeuser the column showing the username
I also determine zesize=column holding the size, and zename=column holding the filename

I'll put the ls command in a variable, so the lines determining the column are using the actual command used later on (in case you change it and it changes the column(s) listed)

2) use sort to sort on that column:

$myls | sort -k${zeuser},${zeuser}  #sort ONLY on column of usernames (see last example for bad alternative)
$myls | sort -k${zeuser},${zeuser} -k${zename},${zename} #sort on user, and then on filename
$myls | sort -k${zeuser},${zeuser} -k${zesize},${zesize}nr #sort on user, 
                            #  and then on size 
                            #modifiers: 'n'=order Numerically (and not alphabetically), 
                            #           'r'=Reverse order
$myls | sort -k${zeuser}    #sort STARTING FROM user column, which is probably not what you want!
                     #indeed the next column is probably the group, then the size...
                     #It will be sorting in a not so usefull way (especially as the
                     #  size will be sorted alphabetically instead of numerically)
share|improve this answer
    
the awk are done for clarity, not tersity... I'm sure a guru out there will propose a much neater/shorter way –  Olivier Dulac Apr 18 '13 at 19:39
    
I get this error at step #2: sort: open failed: 4,3: No such file or directory The value of $zeuser is 3 4 The zeuser assignment doesn't handle when the owner and group names are the same? –  depquid Apr 18 '13 at 19:58
    
@depquid: I don't understand: my 1) part take care of finding the columns using the myls command. Assign myls=the_command_you'll_actually_use and it will be determining the columns for that actual command (be it alias or anything) and then the sorts will be using those found values. –  Olivier Dulac Apr 18 '13 at 20:49
    
@depquid: I edited to break as soon as a match occurs (but if you need the group, it will get a bit more difficult, where you'll need to take the last (=1st if only 1 number returned, or 2nd if 2 number returned in case user=group)) –  Olivier Dulac Apr 18 '13 at 20:53
    
Sorry, I missed the significance of the edit where you added $myls. –  depquid Apr 19 '13 at 16:07

Here's a little one liner that should do it for ya:

\ls -l | sort -k$(for i in {1..5}; do field=$(\ls -ld ~ | cut -d' ' -f$i); if [ x$field = x$(whoami) ]; then echo $i; break; fi; done)

I'm simply walking through each field in an ls -l performed on your home directory until I find the field that matches your user name, and substituting that number in to go with the -k option for sort.

I'm not much of an expert, so some of this may be bash version or GNU specific, but it works fine on my machine.

I chose 1-5 because that should be far as you need to go to find user. You could use more numbers, especially if you saved the output of ls -ld ~ in a string instead of calling each time, and could probably optimize even more if you stored the results into an array and referenced that way. But this was a quick and dirty, off the top of my head, one time use kind of answer.

share|improve this answer
1  
On many systems, the group of ~ has the same name as the user name, so, $field would then be 3 4 there. –  Stéphane Chazelas Apr 18 '13 at 20:03
    
This is wrong for the reasons given in Anthon's answer. If you can't assume that the ls column order is consistent, then you can't assume that ls -ld ~ will return what you think it will. What if a user uses alias ls='ls -g --author'? –  depquid Apr 18 '13 at 20:29
    
Well it was a quick and dirty solution - so often what I do in linux when tackling a solution like this. I didn't think about the group - A break could be added to stop once the first matching field is found (which should be the user), and escaped the ls to avoid alias problems. Still not perfect, but meh, Works on My Machine© –  Drake Clarris Apr 19 '13 at 13:18
    
ls -l | sort -k 3 also "Works on My Machine©", but the OP was looking for a solution that was more robust/portable than that. –  depquid Apr 19 '13 at 15:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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