Lista X archivos aleatorios de un directorio


14

¿Hay alguna manera de enumerar un conjunto de, digamos, 30 archivos aleatorios de un directorio utilizando comandos estándar de Linux? (en zsh)

La respuesta principal descrita aquí no funciona para mí ( sortno reconoce la opción -R)


Tengo curiosidad por saber en qué sistema operativo estás.
ixtmixilix

Respuestas:


7

Como mencionas zsh:

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

Puede reemplazar printcon say ogg123y *con say**/*.ogg


19

Intente canalizar la lssalida a shuf, p. Ej.

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

La -nbandera especifica cuántos archivos aleatorios desea.


command not found(no instalado): /
Amelio Vazquez-Reina

Extraño ... debería ser parte de coreutils. Si su coreutils es viejo, entonces el comando no estará allí.
Renan

66
Esta es la forma rápida y sucia, y también la usaría, para un script único. Para algo más duradero, evite analizar la salida dels .
Janmoesen

@janmoesen No sabía sobre esto. ¡Gracias!
Renan

@ Renan No todos los Unix tienen GNU coreutils instalados por defecto.
Kusalananda

2

Es bastante fácil resolver esto con un poquito de Perl. Seleccione cuatro archivos al azar del directorio actual:

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

Sin embargo, para uso en producción, iría con un script expandido que no depende de la lssalida, puede aceptar cualquier directorio, verifica sus argumentos, etc. Tenga en cuenta que la selección aleatoria en sí misma solo es un par de líneas.

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

En mi opinión, un script Perl no es un "comando estándar de Unix". Este problema es fácil de resolver en cualquier lenguaje de script de alto nivel como Perl, Python o Ruby, pero es más interesante si está directamente en la línea de comandos.
gerrit

1
@gerrit - Entiendo tu punto. Proporcioné esto porque la respuesta preferida (usando shuf) no era suficiente para el OP. En cuanto a qué es y qué no es Unix estándar, Perl existe en todas partes y resuelve el problema de manera sucinta y sólida. Si hubiera dado esto como una línea Perl, ¿eso habría contado como 'línea de comando'? ¿Es el hecho de que Perl es poderoso realmente un argumento en contra de la existencia de esta respuesta?
ire_and_curses

Yo diría que es una respuesta a una pregunta diferente. Por supuesto, en cualquier lenguaje de programación con todas las funciones, este problema es trivial, para mí, la parte interesante de la pregunta es si se puede hacer / sin / recurrir a un script ~ 20 LOC; los lenguajes de secuencias de comandos son poderosos, pero no creo que Perl clasifique como un comando (como lo estaba pidiendo el OP; y no, si hubiera dado una línea de comando de 200 caracteres invocando a Perl, habría tenido la misma opinión).
gerrit

@gerrit - Quizás pueda ser más claro. Echa un vistazo a mi edición: perl -MList::Util=shuffle -e 'print shuffle(ls )' | head -n 4¿Se parece más a lo que estabas buscando?
ire_and_curses

Sí, eliminé mi voto negativo, que ciertamente fue un poco duro.
gerrit

1

Una solución simple que evita el análisis de ls y también funciona con espacios:

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

Como se ve en otros comentarios, el OP está en un sistema sin shuf.
Kusalananda

Evitar shufno se menciona en la pregunta. Esta respuesta seguirá siendo útil para otros usuarios.
scai

0

Un oneliner que usa nada más que Zsh:

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

Lo mismo en Bash, donde los índices de matriz están basados ​​en cero:

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

Tenga en cuenta que ninguna de las versiones tiene en cuenta los duplicados.


1
Esto puede enumerar el mismo archivo más de una vez. Evitar esto es una parte importante del problema.
Gilles 'SO- deja de ser malvado'

Gilles: muy cierto, por eso dije "Tenga en cuenta que ninguna de las versiones tiene en cuenta los duplicados". Todavía podría hacerlo en Bash puro, si no le importa la complejidad del tiempo y reconstruye la matriz cada vez que elimina una aleatoria. Ni siquiera cerca de una línea, entonces, por supuesto, eso no era un requisito.
Janmoesen
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.