Liste X fichiers aléatoires d'un répertoire

14

Existe-t-il un moyen de répertorier un ensemble de, disons, 30 fichiers aléatoires à partir d'un répertoire à l'aide de commandes Linux standard? (en zsh)

La première réponse décrite ici ne fonctionne pas pour moi ( sortne reconnaît pas l'option -R)

Amelio Vazquez-Reina
la source
je suis curieux de savoir quel OS vous utilisez.
ixtmixilix

Réponses:

7

Puisque vous mentionnez zsh:

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

Vous pouvez remplacer printpar dire ogg123et *par dire**/*.ogg

Stéphane Chazelas
la source
19

Essayez de diriger la lssortie vers shuf, par exemple

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

L' -nindicateur spécifie le nombre de fichiers aléatoires que vous souhaitez.

Renan
la source
command not found(non installé): /
Amelio Vazquez-Reina
Étrange ... ça devrait faire partie des coreutils. Si votre coreutils est ancien, alors la commande ne sera pas là.
Renan
6
C'est le moyen rapide et sale, et je l'utiliserais aussi - pour un script unique. Pour quelque chose de plus durable, évitez d'analyser la sortie dels .
janmoesen
@janmoesen Je n'en savais rien. Merci!
Renan
@Renan Tous les Unix n'ont pas de coreutils GNU installés par défaut.
Kusalananda
2

Il est assez facile de résoudre ce problème avec un tout petit peu de Perl. Sélectionnez quatre fichiers au hasard dans le répertoire actuel:

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

Cependant, pour une utilisation en production, j'irais avec un script développé qui ne dépend pas de la lssortie, peut accepter n'importe quel répertoire, vérifie vos arguments, etc. Notez que la sélection aléatoire elle-même n'est toujours que de quelques lignes.

#!/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";
}
ire_and_curses
la source
À mon humble avis, un script Perl n'est pas une «commande Unix standard». Ce problème est facile à résoudre dans n'importe quel langage de script de haut niveau tel que Perl, Python ou Ruby, mais plus intéressant s'il est directement sur la ligne de commande.
gerrit
1
@gerrit - Je comprends votre point. J'ai fourni cela parce que la réponse préférée (en utilisant shuf) n'était pas suffisante pour l'OP. Quant à ce qui est et n'est pas standard Unix - Perl existe partout, et il résout le problème de manière succincte et robuste. Si j'avais donné cela comme une ligne Perl, cela aurait-il compté comme «ligne de commande»? Le fait que Perl soit puissant est-il vraiment un argument contre l'existence de cette réponse?
ire_and_curses
Je dirais que c'est une réponse à une question différente. Bien sûr, dans tout langage de programmation complet, ce problème est trivial, pour moi la partie intéressante de la question est de savoir s'il peut être fait / sans / recourir à un script LOC ~ 20; les langages de script sont puissants, mais je ne pense pas que Perl se classe comme une commande (comme l'OP le demandait; et non, si vous aviez donné une ligne de commande de 200 caractères invoquant Perl, j'aurais eu la même opinion).
gerrit
@gerrit - Je peux peut-être être plus clair. Jetez un oeil à mon montage: perl -MList::Util=shuffle -e 'print shuffle(ls )' | head -n 4Est-ce que cela ressemble plus à ce que vous cherchiez?
ire_and_curses
Ouais, j'ai supprimé mon downvote qui était certes un peu dur.
gerrit
1

Une solution simple qui évite l'analyse de ls et fonctionne également avec les espaces:

shuf -en 30 dir/* | while read file; do
    echo $file
done
scai
la source
Comme on l'a vu dans d'autres commentaires, le PO est sur un système sans shuf.
Kusalananda
Éviter shufn'est pas mentionné dans la question. Cette réponse sera toujours utile aux autres utilisateurs.
scai
0

Un oneliner n'utilisant que Zsh:

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

La même chose dans Bash, où les indices de tableau sont basés sur zéro:

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

Notez qu'aucune des versions ne prend en compte les doublons.

janmoesen
la source
1
Cela peut répertorier le même fichier plusieurs fois. Éviter cela est une partie importante du problème.
Gilles 'SO- arrête d'être méchant'
Gilles: tout à fait raison, c'est pourquoi j'ai dit "Notez qu'aucune des versions ne prend en compte les doublons." Vous pouvez toujours le faire en pur Bash, si vous ne vous souciez pas de la complexité du temps et reconstruisez le tableau à chaque fois que vous en supprimez un aléatoire. Pas même près d'un monoplace, alors, mais bien sûr, ce n'était pas une exigence.
janmoesen