Escolhendo um arquivo aleatório de cada subpasta

2

Inspirado pela resposta aceita a este tópico: Listar arquivos aleatórios X de um diretório , eu tentei juntar duas linhas que selecionariam um arquivo de cada subpasta do diretório atual, sem sucesso:

rand() REPLY=$RANDOM
for x in *; do y=$($x/*(o+rand[1])); print ${y:r}; done

Parece que a expansão de y não está funcionando corretamente. Por quê?

O que há de errado com o código acima?

    
por Amelio Vazquez-Reina 31.01.2013 / 02:23

2 respostas

2

Não é a expansão de y , mas o valor atribuído a y é o problema.

Você tem isso

y=$($x/*(o+rand[1]))

O valor atribuído é o resultado da substituição do comando ( $(…) ). A única “palavra” no comando substituído é a expressão glob. Isso significa que ele tentará executar o arquivo selecionado como um comando (o que provavelmente causará um erro e não produzirá nenhuma saída útil).

Para fazê-lo funcionar (embora com uma mudança na semântica), você pode alterar sua atribuição de substituição de comando em uma atribuição de matriz (por exemplo, remover o $ após o = ):

y=($x/*(o+rand[1]))

Se você quiser manter a substituição do comando, você precisa fazer com que ele exiba o nome do caminho selecionado em vez de tentar executá-lo como um comando:

y=$(print -- $x/*(o+rand[1]))

Mas há um problema oculto aqui. Cada glob será avaliado em um subshell separado, portanto, cada diretório usará a mesma sequência de números aleatórios para classificar suas entradas. Você pode observar isso se usar set -x e observar a sequência de valores atribuídos a REPLY para cada diretório. Isso significa que os arquivos não serão selecionados independentemente entre diretórios.

Aqui estão algumas abordagens alternativas que você pode usar para atribuir uma expansão glob a um parâmetro (elas mantêm a avaliação $RANDOM no mesmo shell, portanto as seleções serão (mais) independentes):

for y in $x/*(o+rand[1]); do print -- ${y:r}; done
set -- $x/*(o+rand[1]); print -l -- ${@:r}

Se você deseja manipular diretórios vazios, provavelmente deve adicionar o qualificador N glob (age como se NULL_GLOB estivesse definido). Isso também ajudará a manipular diretórios não correspondentes ao glob externo (embora você possa usar o qualificador / glob para limitar esse apenas aos diretórios).

rand() REPLY=$RANDOM
for x in *(/); do y=($x/*(No+rand[1])); print -l -- ${y:r}; done
    
por 31.01.2013 / 07:55
3

Você pode adicionar modificadores a qualificadores de globbing:

for x (*(/)) print -rl -- $x/*(No+rand[1]:r)

Veja também:

print -rl -- *(N/e{'REPLY=($REPLY/*(No+rand[1]:r))'})
    
por 31.01.2013 / 15:08