Jak Mogę wybrać losowe pliki z katalogu w bash?

Mam katalog z około 2000 plikami. Jak Mogę wybrać losową próbkę plików N za pomocą skryptu bash lub listy poleceń piped?

 165
Author: nickb, 2009-01-05

12 answers

Oto skrypt, który używa opcji losowej GNU sort:

ls |sort -R |tail -$N |while read file; do
    # Something involving $file, or you can leave
    # off the while to just get the filenames
done
 196
Author: Josh Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-05 20:01:13

Możesz do tego użyć shuf (z pakietu GNU coreutils). Po prostu podaj mu listę nazw plików i poproś o zwrócenie pierwszej linii z losowej permutacji:

ls dirname | shuf -n 1
# probably faster and more flexible:
find dirname -type f | shuf -n 1
# etc..

Dostosuj wartość -n, --head-count=COUNT, aby zwrócić liczbę poszukiwanych linii. Na przykład, aby zwrócić 5 losowych nazw plików, należy użyć:

find dirname -type f | shuf -n 5
 121
Author: Nordic Mainframe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-01 20:16:08

Oto kilka możliwości, które nie analizują wyjścia ls i które są w 100% bezpieczne w odniesieniu do plików ze spacjami i zabawnymi symbolami w ich nazwie. Wszystkie z nich wypełnią tablicę randf listą losowych plików. Tablicę można łatwo wydrukować za pomocą printf '%s\n' "${randf[@]}" w razie potrzeby.

  • Ten prawdopodobnie wyświetli ten sam plik kilka razy i N musi być wcześniej znany. Tutaj wybrałem N = 42.

    a=( * )
    randf=( "${a[RANDOM%${#a[@]}]"{1..42}"}" )
    

    Ta funkcja nie jest zbyt dobra udokumentowane.

  • Jeśli N nie jest znane z góry, ale naprawdę podobała ci się poprzednia możliwość, możesz użyć eval. Ale to jest złe i musisz naprawdę upewnić się, że N nie pochodzi bezpośrednio z danych Użytkownika bez dokładnego sprawdzenia!

    N=42
    a=( * )
    eval randf=( \"\${a[RANDOM%\${#a[@]}]\"\{1..$N\}\"}\" )
    

    ja osobiście nie lubię eval i stąd ta odpowiedź!

  • To samo przy użyciu prostszej metody (pętli):

    N=42
    a=( * )
    randf=()
    for((i=0;i<N;++i)); do
        randf+=( "${a[RANDOM%${#a[@]}]}" )
    done
    
  • Jeśli nie chcesz mieć kilka razy ten sam plik:

    N=42
    a=( * )
    randf=()
    for((i=0;i<N && ${#a[@]};++i)); do
        ((j=RANDOM%${#a[@]}))
        randf+=( "${a[j]}" )
        a=( "${a[@]:0:j}" "${a[@]:j+1}" )
    done
    

Uwaga . Jest to późna odpowiedź na stary post, ale zaakceptowana odpowiedź łączy się z zewnętrzną stroną, która pokazuje straszną praktykębash , a druga odpowiedź nie jest dużo lepsza, ponieważ również parsuje wyjście ls. Lhunath nie jest w stanie znaleźć odpowiedzi na pytanie, czy jest w stanie odpowiedzieć na pytanie, czy jest w stanie odpowiedzieć na pytanie, czy jest w stanie odpowiedzieć na pytanie, czy jest w stanie odpowiedzieć na pytanie, czy jest w stanie odpowiedzieć na pytanie, czy jest w stanie odpowiedzieć na pytanie, czy jest w stanie odpowiedzieć na pytanie.]}

 19
Author: gniourf_gniourf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-01 18:08:49
ls | shuf -n 10 # ten random files
 14
Author: silgon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-15 07:55:05

Proste rozwiązanie do wybierania 5 losowych plików podczasunikania parsowania ls . Działa również z plikami zawierającymi spacje, nowe linie i inne znaki specjalne:

shuf -ezn 5 * | xargs -0 -n1 echo

Zastąp echo poleceniem, które chcesz wykonać dla swoich plików.

 9
Author: scai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-18 11:48:34

Jest to jeszcze późniejsza odpowiedź na późną odpowiedź @gniourf_gniourf, którą właśnie podniosłem, ponieważ jest to zdecydowanie najlepsza odpowiedź, dwa razy. (Raz za unikanie eval i raz za bezpieczną obsługę nazw plików.)

Ale zajęło mi kilka minut, aby rozwikłać" nie bardzo dobrze udokumentowane " funkcja(y) ta odpowiedź używa. Jeśli twoje umiejętności Bash są na tyle solidne, że natychmiast widziałeś, jak to działa, pomiń ten komentarz. Ale nie zrobiłem tego i po rozplątaniu myślę, że warto wyjaśniam.

Funkcja # 1 jest globbingiem plików własnych powłoki. a=(*) tworzy tablicę $a, której członkowie są plikami w bieżącym katalogu. Bash rozumie wszystkie dziwactwa nazw plików, więc lista jest gwarantowana poprawna, gwarantowana Ucieczka itp. Nie musisz się martwić o poprawne parsowanie nazw plików tekstowych zwracanych przez ls.

Funkcja # 2 to rozszerzenia parametrów Bash dla tablic , jedna zagnieżdżona w drugiej. To się zaczyna z ${#ARRAY[@]}, która rozszerza się na długość $ARRAY.

To rozszerzenie jest następnie używane do indeksowania tablicy. Standardowym sposobem znalezienia liczby losowej od 1 do N jest wzięcie wartości liczby losowej modulo N. chcemy liczby losowej od 0 do długości naszej tablicy. Oto podejście, podzielone na dwie linie dla jasności: {]}

LENGTH=${#ARRAY[@]}
RANDOM=${a[RANDOM%$LENGTH]}

Ale To rozwiązanie robi to w jednej linii, usuwając niepotrzebne przypisanie zmiennej.

Funkcja # 3 jest rozszerzenie Bash brace, chociaż muszę przyznać, że nie do końca to Rozumiem. Rozszerzenie Brace służy np. do wygenerowania listy 25 plików o nazwie filename1.txt, filename2.txt, itd: echo "filename"{1..25}".txt".

Wyrażenie wewnątrz podshellu powyżej, "${a[RANDOM%${#a[@]}]"{1..42}"}", używa tej sztuczki do wytworzenia 42 oddzielnych rozszerzeń. Rozszerzenie nawiasu umieszcza pojedynczą cyfrę pomiędzy ] i }, co początkowo myślałem, że jest zapisem tablicy, ale jeśli tak, to będzie poprzedzone dwukropkiem. (Byłoby zwróć również 42 kolejne elementy z losowego miejsca w tablicy, co wcale nie jest tym samym, co zwracanie 42 losowych elementów z tablicy.) Myślę, że po prostu sprawia, że powłoka uruchamia rozszerzenie 42 razy, tym samym zwracając 42 losowe elementy z tablicy. (Ale jeśli ktoś potrafi to wyjaśnić pełniej, to chętnie posłucham.)

Powodem, dla którego N musi być zakodowane na twardo (do 42) jest to, że rozszerzenie nawiasów następuje przed rozszerzeniem zmiennej.

Wreszcie, Oto Funkcja # 4 , jeśli chcesz to zrobić rekurencyjnie dla hierarchii katalogów:

shopt -s globstar
a=( ** )

To włącza opcję powłoki, która powoduje, że ** dopasowuje się rekurencyjnie. Teraz Twoja tablica $a zawiera każdy plik w całej hierarchii.

 5
Author: Ken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-01 23:58:55

Jeśli masz zainstalowany Python (działa z Pythonem 2 lub Pythonem 3):

Aby wybrać jeden plik (lub linię z dowolnego polecenia), użyj

ls -1 | python -c "import sys; import random; print(random.choice(sys.stdin.readlines()).rstrip())"

Aby wybrać N pliki/linie, użyj (Uwaga N znajduje się na końcu polecenia, zastąp ją liczbą)

ls -1 | python -c "import sys; import random; print(''.join(random.sample(sys.stdin.readlines(), int(sys.argv[1]))).rstrip())" N
 4
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-03 21:14:58

Jeśli masz więcej plików w swoim folderze, możesz użyć poniższego polecenia piped, które znalazłem w Uniksie stackexchange .

find /some/dir/ -type f -print0 | xargs -0 shuf -e -n 8 -z | xargs -0 cp -vt /target/dir/

Tutaj chciałem skopiować pliki, ale jeśli chcesz przenieść pliki lub zrobić coś innego, po prostu zmień ostatnią komendę gdzie użyłem cp.

 2
Author: Bhaskar Chakradhar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-28 13:12:51

To jest jedyny skrypt, który mogę uzyskać, aby grać ładnie z bash na MacOS. Połączyłem i edytowałem fragmenty z następujących dwóch linków:

Polecenie Ls: jak Mogę uzyskać rekurencyjną listę pełnej ścieżki, po jednej linii na plik?

Http://www.linuxquestions.org/questions/linux-general-1/is-there-a-bash-command-for-picking-a-random-file-678687/

#!/bin/bash

# Reads a given directory and picks a random file.

# The directory you want to use. You could use "$1" instead if you
# wanted to parametrize it.
DIR="/path/to/"
# DIR="$1"

# Internal Field Separator set to newline, so file names with
# spaces do not break our script.
IFS='
'

if [[ -d "${DIR}" ]]
then
  # Runs ls on the given dir, and dumps the output into a matrix,
  # it uses the new lines character as a field delimiter, as explained above.
  #  file_matrix=($(ls -LR "${DIR}"))

  file_matrix=($(ls -R $DIR | awk '; /:$/&&f{s=$0;f=0}; /:$/&&!f{sub(/:$/,"");s=$0;f=1;next}; NF&&f{ print s"/"$0 }'))
  num_files=${#file_matrix[*]}

  # This is the command you want to run on a random file.
  # Change "ls -l" by anything you want, it's just an example.
  ls -l "${file_matrix[$((RANDOM%num_files))]}"
fi

exit 0
 1
Author: benmarbles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:32

MacOS nie posiada poleceń sort-R i shuf, więc potrzebowałem tylko rozwiązania bash, które randomizuje wszystkie pliki bez duplikatów i nie znalazłem tego tutaj. To rozwiązanie jest podobne do rozwiązania gniourf_gniourf #4, ale mam nadzieję, że dodaje lepsze komentarze.

Skrypt powinien być łatwy do modyfikacji, aby zatrzymać po N próbkach używając licznika Z if, lub gniourf_gniourf ' s for loop z N. $ RANDOM jest ograniczony do ~32000 plików, ale powinno to wystarczyć dla większości sprawy.

#!/bin/bash

array=(*)  # this is the array of files to shuffle
# echo ${array[@]}
for dummy in "${array[@]}"; do  # do loop length(array) times; once for each file
    length=${#array[@]}
    randomi=$(( $RANDOM % $length ))  # select a random index

    filename=${array[$randomi]}
    echo "Processing: '$filename'"  # do something with the file

    unset -v "array[$randomi]"  # set the element at index $randomi to NULL
    array=("${array[@]}")  # remove NULL elements introduced by unset; copy array
done
 1
Author: cat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-17 11:13:00

Używam tego: używa pliku tymczasowego, ale idzie głęboko w katalogu, aż znajdzie zwykły plik i zwróci go.

# find for a quasi-random file in a directory tree:

# directory to start search from:
ROOT="/";  

tmp=/tmp/mytempfile    
TARGET="$ROOT"
FILE=""; 
n=
r=
while [ -e "$TARGET" ]; do 
    TARGET="$(readlink -f "${TARGET}/$FILE")" ; 
    if [ -d "$TARGET" ]; then
      ls -1 "$TARGET" 2> /dev/null > $tmp || break;
      n=$(cat $tmp | wc -l); 
      if [ $n != 0 ]; then
        FILE=$(shuf -n 1 $tmp)
# or if you dont have/want to use shuf:
#       r=$(($RANDOM % $n)) ; 
#       FILE=$(tail -n +$(( $r + 1 ))  $tmp | head -n 1); 
      fi ; 
    else
      if [ -f "$TARGET"  ] ; then
        rm -f $tmp
        echo $TARGET
        break;
      else 
        # is not a regular file, restart:
        TARGET="$ROOT"
        FILE=""
      fi
    fi
done;
 0
Author: bzimage,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-15 09:24:07

A może jakieś rozwiązanie Perla nieco sfałszowane od Pana Kanga?:
Jak mogę przetasować wiersze pliku tekstowego w wierszu poleceń systemu Unix lub w skrypcie powłoki?

$ LS / perl-mlist:: Util=shuffle-e ' @ lines = shuffle (); print @ lines[0..4]'

 -1
Author: AAAfarmclub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-06 02:11:37