Listar arquivos aleatórios X de um diretório

14

Existe uma maneira de listar um conjunto de, digamos, 30 arquivos aleatórios de um diretório usando comandos padrão do Linux? (in zsh)

A resposta principal descrita aqui não funciona para mim ( sortnão reconhece a opção -R)

Amelio Vazquez-Reina
fonte
Estou curioso em que SO você está.
Ixtmixilix 18/09/12

Respostas:

7

Desde que você está mencionando zsh:

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

Você pode substituir printpor dizer ogg123e *dizer**/*.ogg

Stéphane Chazelas
fonte
19

Tente canalizar a lssaída para shuf, por exemplo

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

O -nsinalizador especifica quantos arquivos aleatórios você deseja.

Renan
fonte
command not found(não instalado): /
Amelio Vazquez-Reina
Estranho ... deveria fazer parte do coreutils. Se o seu coreutils for antigo, o comando não estará lá.
Renan
6
Essa é a maneira mais rápida e suja, e eu a usaria também - para um script único. Para algo mais durável, evite analisar a saída dels .
janmoesen
@janmoesen Eu não sabia disso. Obrigado!
Renan
@Renan Nem todos os Unix têm o núcleo GNU instalado por padrão.
Kusalananda
2

É muito fácil resolver isso com um pouquinho de Perl. Selecione quatro arquivos aleatoriamente no diretório atual:

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

No entanto, para uso em produção, eu usaria um script expandido que não depende da lssaída, pode aceitar qualquer dir, verifica seus argumentos, etc. Observe que a seleção aleatória em si ainda tem apenas algumas linhas.

#!/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
fonte
Imho um script Perl não é um 'comando unix padrão'. Esse problema é fácil de resolver em qualquer linguagem de script de alto nível, como Perl, Python ou Ruby, mas é mais interessante se estiver diretamente na linha de comando.
gerrit
1
@gerrit - Entendo o seu ponto. Forneci isso porque a resposta preferida (usando shuf) não era suficiente para o OP. Quanto ao que é e não é padrão, o Unix - Perl existe em todos os lugares e resolve o problema de maneira sucinta e robusta. Se eu tivesse dado isso como uma linha única do Perl, isso contaria como 'linha de comando'? O fato de Perl ser poderoso é realmente um argumento contra a existência dessa resposta?
#
Eu diria que é uma resposta para uma pergunta diferente. É claro que em qualquer linguagem de programação com todos os recursos esse problema é trivial, para mim a parte interessante da pergunta é se ela pode ser feita / sem / recorrendo a um script ~ 20 LOC; linguagens de script são poderosas, mas não acho que o Perl classifique como um comando (como o OP estava pedindo; e não, se você tivesse dado uma linha de comando de 200 caracteres invocando o Perl, teria a mesma opinião).
Gerrit 18/09/12
@gerrit - Talvez eu possa ser mais claro. Dê uma olhada na minha edição: perl -MList::Util=shuffle -e 'print shuffle(ls )' | head -n 4É mais parecido com o que você estava procurando?
ire_and_curses
Sim, removi meu voto negativo, que reconhecidamente foi um pouco duro.
gerrit
1

Uma solução simples que evita a análise de ls e também trabalha com espaços:

shuf -en 30 dir/* | while read file; do
    echo $file
done
scai
fonte
Como visto em outros comentários, o OP está em um sistema sem shuf.
Kusalananda
Evitar shufnão é mencionado na pergunta. Esta resposta ainda será útil para outros usuários.
Scai
0

Um oneliner usando nada além de Zsh:

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

O mesmo no Bash, onde os índices da matriz são baseados em zero:

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

Observe que nenhuma das versões leva em consideração as duplicatas.

janmoesen
fonte
1
Isso pode listar o mesmo arquivo mais de uma vez. Evitar isso é uma parte significativa do problema.
Gilles 'SO- stop be evil'
Gilles: está certo, e foi por isso que eu disse "Observe que nenhuma das versões leva em consideração as duplicatas". Você ainda pode fazer isso no Bash puro, se não se importar com a complexidade do tempo e reconstruir a matriz sempre que remover uma aleatória. Nem mesmo perto de uma fila, então, mas é claro que isso não era um requisito.
janmoesen 22/09/12