Elenca X file casuali da una directory


14

C'è un modo per elencare una serie di 30 file casuali da una directory usando i comandi standard di Linux? (in zsh)

La risposta migliore qui descritta non funziona per me ( sortnon riconosce l'opzione -R)


sono curioso di sapere quale sistema operativo sei su.
ixtmixilix,

Risposte:


7

Dato che stai citando zsh:

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

È possibile sostituire printcon dire ogg123e *con dire**/*.ogg


19

Prova a collegare l' lsoutput a shuf, ad es

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

Il -nflag specifica quanti file casuali vuoi.


command not found(non installato): /
Amelio Vazquez-Reina,

Strano ... dovrebbe essere una parte dei coreutils. Se il tuo coreutils è vecchio, il comando non sarà presente.
Renan,

6
Questo è il modo rapido e sporco, e lo userei anche io - per uno script unico. Per qualcosa di più durevole, evitare di analizzare l'output dils .
Janmoesen,

@janmoesen Non lo sapevo. Grazie!
Renan,

@Renan Non tutti gli Unix hanno coreutils GNU installati di default.
Kusalananda

2

È abbastanza facile risolverlo con un po 'di Perl. Seleziona quattro file a caso dalla directory corrente:

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

Per l'uso in produzione, però, andrei con uno script espanso che non si basa lssull'output, può accettare qualsiasi directory, controllare i tuoi argomenti, ecc. Nota che la selezione casuale stessa è ancora solo un paio di righe.

#!/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";
}

Imho uno script Perl non è un "comando unix standard". Questo problema è facile da risolvere in qualsiasi linguaggio di scripting di alto livello come Perl, Python o Ruby, ma più interessante se è direttamente dalla riga di comando.
Gerrit,

1
@gerrit - Prendo il tuo punto. Ho fornito questo perché la risposta preferita (utilizzo shuf) non era sufficiente per il PO. Quanto a ciò che è e non è lo standard Unix: il Perl esiste ovunque e risolve il problema in modo succinto e solido. Se avessi dato questo come un one-liner Perl, sarebbe stato considerato come "riga di comando"? Il fatto che Perl sia potente è davvero un argomento contro l'esistenza di questa risposta?
ire_and_curses,

Direi che è una risposta a una domanda diversa. Naturalmente in qualsiasi linguaggio di programmazione completo questo problema è banale, per me la parte interessante della domanda è se può essere fatto / senza / ricorrere a uno script LOC di ~ 20; i linguaggi di scripting sono potenti, ma non credo che Perl si classifichi come un comando (come richiesto dall'OP; e no, se avessi dato una linea di comando di 200 caratteri che invocava Perl avrei avuto la stessa opinione).
Gerrit,

@gerrit - Forse posso essere più chiaro. Date un'occhiata a mia modifica: perl -MList::Util=shuffle -e 'print shuffle(ls )' | head -n 4È questo più simile a quello che cercavi?
ire_and_curses,

Sì, ho rimosso il mio voto negativo, che è stato certamente un po 'duro.
Gerrit,

1

Una soluzione semplice che evita l'analisi di ls e funziona anche con gli spazi:

shuf -en 30 dir/* | while read file; do
    echo $file
done

Come visto in altri commenti, l'OP è su un sistema senza shuf.
Kusalananda

Evitare shufnon è menzionato nella domanda. Questa risposta sarà comunque utile ad altri utenti.
scai,

0

Un oneliner che non usa altro che Zsh:

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

Lo stesso in Bash, dove gli indici di array sono a base zero:

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

Si noti che nessuna versione prende in considerazione i duplicati.


1
Questo può elencare lo stesso file più di una volta. Evitare questo è una parte significativa del problema.
Gilles 'SO- smetti di essere malvagio' il

Gilles: assolutamente giusto, motivo per cui ho detto "Nota che nessuna versione tiene conto dei duplicati". Potresti ancora farlo in puro Bash, se non ti interessa la complessità del tempo e ricostruire l'array ogni volta che ne rimuovi uno casuale da esso. Nemmeno vicino a un liner, quindi, ma ovviamente non era un requisito.
Janmoesen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.