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.

Is there a way to list a set of say, 30 random files from a directory using standard Linux commands? (in zsh)

The top answer described here does not work for me (sort does not recognize the option -R)

share|improve this question
    
i'm curious what OS you're on. –  ixtmixilix Sep 18 '12 at 22:03
add comment

4 Answers

up vote 4 down vote accepted

Since you're mentioning zsh:

rand() REPLY=$RANDOM
print -rl -- *(o+rand[1,30])

You can replace print with say ogg123 and * with say **/*.ogg

share|improve this answer
add comment

Try piping the ls output to shuf, e.g.

$ touch 1 2 3 4 5 6 7 8 9 0
$ ls | shuf -n 5
5
9
0 
8
1

The -n flag specifies how many random files you want.

share|improve this answer
    
command not found (not installed) :/ –  user815423426 Sep 18 '12 at 2:23
    
Strange... it should be a part of coreutils. If your coreutils is old, then the command won't be there. –  Renan Sep 18 '12 at 2:24
4  
This is the quick and dirty way, and I would use it, too — for a one-off script. For anything more durable, avoid parsing the output of ls. –  janmoesen Sep 18 '12 at 15:53
    
@janmoesen I didn't know about this. Thanks! –  Renan Sep 18 '12 at 15:55
add comment

It's quite easy to solve this with a tiny bit of Perl. Select four files at random from the current directory:

perl -MList::Util=shuffle -e 'print shuffle(`ls`)' | head -n 4

For production use though, I would go with an expanded script that doesn't rely on ls output, can accept any dir, checks your args, etc. Note that the random selection itself is still only a couple of lines.

#!/usr/bin/perl    
use strict;
use warnings;
use List::Util qw( shuffle );

if ( @ARGV < 2 ) {
    die "$0 - List n random files from a directory\n"
        . "Usage: perl $0 n dir\n";
}
my $n_random = shift;
my $dir_name = shift;
opendir(my $dh, $dir_name) || die "Can't open directory $dir_name: $!";

# Read in the filenames in the directory, skipping '.' and '..'
my @filenames = grep { !/^[.]{1,2}$/ } readdir($dh);
closedir $dh;

# Handle over-specified input
if ( $n_random > $#filenames ) {
    print "WARNING: More values requested ($n_random) than available files ("
          . @filenames . ") - truncating list\n";
    $n_random = @filenames;
}

# Randomise, extract and print the chosen filenames
foreach my $selected ( (shuffle(@filenames))[0..$n_random-1] ) {
    print "$selected\n";
}
share|improve this answer
    
Imho a Perl-script is not a 'standard unix command'. This problem is easy to solve in any high-level scripting language such as Perl, Python or Ruby, but more interesting if it's directly on the commandline. –  gerrit Sep 18 '12 at 7:42
1  
@gerrit - I take your point. I provided this because the preferred answer (using shuf) was not sufficient for the OP. As to what is and isn't standard Unix - Perl exists everywhere, and it solves the problem succinctly and robustly. If I'd given this as a Perl one-liner, would that have counted as 'command line'? Is the fact Perl is powerful really an argument against the existence of this answer? –  ire_and_curses Sep 18 '12 at 14:01
    
I'd say it's an answer to a different question. Of course in any fully-featured programming language this problem is trivial, for me the interesting part of the question is if it can be done /without/ resorting to a ~20 LOC script; scripting languages are powerful, but I don't think Perl classifies as a command (such as the OP was asking for; and no, if you'd given a 200-character commandline invoking Perl I'd have had the same opinion). –  gerrit Sep 18 '12 at 14:39
    
@gerrit - Perhaps I can be clearer. Take a look at my edit: perl -MList::Util=shuffle -e 'print shuffle(ls`)' | head -n 4` Is this more like what you were looking for? –  ire_and_curses Sep 18 '12 at 15:02
    
Yeah, I removed my downvote which admittedly was a bit harsh. –  gerrit Sep 18 '12 at 15:28
add comment

A oneliner using nothing but Zsh:

files=(*); for x in {1..30}; do i=$((RANDOM % ${#files[@]} + 1)); echo "${files[i]}"; done

The same in Bash, where array indices are zero-based:

files=(*); for x in {1..30}; do i=$((RANDOM % ${#files[@]})); echo "${files[i]}"; done

Note that neither version takes duplicates into account.

share|improve this answer
1  
This may list the same file more than once. Avoiding this is a significant part of the problem. –  Gilles Sep 18 '12 at 23:40
    
Gilles: quite right, which is why I said "Note that neither version takes duplicates into account." You could still do it in pure Bash, if you do not care about time complexity and rebuild the array each time you remove a random one from it. Not even close to a one-liner, then, but of course that was not a requirement. –  janmoesen Sep 22 '12 at 20:54
add comment

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.