Yaklaşık 2000 dosya içeren bir dizin var. NBir bash betiği veya borulu komutların bir listesini kullanarak rastgele bir dosya örneği nasıl seçebilirim ?
ls | shuf -n 5 Kaynak Unix Stackexchange
Yaklaşık 2000 dosya içeren bir dizin var. NBir bash betiği veya borulu komutların bir listesini kullanarak rastgele bir dosya örneği nasıl seçebilirim ?
ls | shuf -n 5 Kaynak Unix Stackexchange
Yanıtlar:
İşte GNU sort'in rastgele seçeneğini kullanan bir script:
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
"$file"gösterilmemesi, kullanımı boşluklara karşı hassas olacaktır.
Bunun için shuf(GNU coreutils paketinden) kullanabilirsiniz. Sadece dosya adlarının bir listesini verin ve ilk satırı rastgele bir permütasyondan döndürmesini isteyin:
ls dirname | shuf -n 1
# probably faster and more flexible:
find dirname -type f | shuf -n 1
# etc..
İstenen -n, --head-count=COUNTsatır sayısını döndürmek için değeri ayarlayın . Örneğin, kullanacağınız 5 rastgele dosya adı döndürmek için:
find dirname -type f | shuf -n 5
Nrastgele dosyaları seçmek istedi , bu yüzden kullanmak 1biraz yanıltıcı.
find dirname -type f -print0 | shuf -zn1
İşte çıktılarını ayrıştırmayan ve lsadlarında boşluk ve komik sembollere sahip dosyalar için% 100 güvenli olan birkaç olasılık . Hepsi bir dizi randfrastgele dosya listesiyle doldurulur . Bu dizi printf '%s\n' "${randf[@]}"gerektiğinde kolayca yazdırılır .
Bu dosya aynı dosyayı birkaç kez Nçıkarır ve önceden bilinmesi gerekir. Burada N = 42'yi seçtim.
a=( * )
randf=( "${a[RANDOM%${#a[@]}]"{1..42}"}" )
Bu özellik çok iyi belgelenmemiştir.
N önceden bilinmiyorsa, ancak önceki olasılığı gerçekten beğendiyseniz, kullanabilirsiniz eval. Ama bu kötü ve gerçekten Nkontrol edilmeden doğrudan kullanıcı girişinden gelmediğinden emin olmalısınız !
N=42
a=( * )
eval randf=( \"\${a[RANDOM%\${#a[@]}]\"\{1..$N\}\"}\" )
Ben şahsen beğenmedim evalve dolayısıyla bu cevabı!
Aynı şey daha basit bir yöntem (bir döngü) kullanarak:
N=42
a=( * )
randf=()
for((i=0;i<N;++i)); do
randf+=( "${a[RANDOM%${#a[@]}]}" )
doneAynı dosyanın birkaç katına sahip olmak istemiyorsanız:
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}" )
doneNot . Bu eski bir gönderiye geç bir cevaptır, ancak kabul edilen yanıt korkunç gösteren harici bir sayfaya bağlanırdarbeve diğer yanıtı da çıktısını ayrıştırdığı için çok daha iyi değildir ls. Kabul edilen cevaba yapılan bir yorum, Lhunath'ın iyi bir uygulama gösteren, ancak OP'ye tam olarak cevap vermeyen mükemmel bir cevaba işaret ediyor.
"{1..42}"bir iz bırakan kısmı beğenmedi "1". Ayrıca, $RANDOMsadece 15 bit ve yöntem 32767'den fazla dosya ile çalışmaz.
ls | shuf -n 10 # ten random files
ls. Örneğin, bir dosya adı yeni satır içeriyorsa bu çalışmaz.
lssize "temiz" dosya adları vermeniz garanti edilmez, bu yüzden ona güvenmemelisiniz, nokta. Bu sorunların nadir veya olağandışı olması sorunu değiştirmez; özellikle bunun için daha iyi çözümler var.
lsdizinler ve boş satırlar içerebilir. Bunun find . -type f | shuf -n10yerine böyle bir şey öneririm .
Ls ayrıştırmaktan kaçınırken5 rastgele dosyaları seçmek için basit bir çözüm . Ayrıca boşluklar, yeni satırlar ve diğer özel karakterler içeren dosyalarla da çalışır:
shuf -ezn 5 * | xargs -0 -n1 echo
echoDosyalarınız için yürütmek istediğiniz komutla değiştirin .
readayrıştırma ile aynı sorunlara sahip değil lsmi? yani satır satır okur, bu yüzden
Python yüklüyse (Python 2 veya Python 3 ile çalışır):
Bir dosya (veya rastgele bir komuttan satır) seçmek için
ls -1 | python -c "import sys; import random; print(random.choice(sys.stdin.readlines()).rstrip())"
NDosyaları / satırları seçmek için şunu kullanın (not Nkomutun sonundadır, bunu bir sayı ile değiştirin)
ls -1 | python -c "import sys; import random; print(''.join(random.sample(sys.stdin.readlines(), int(sys.argv[1]))).rstrip())" N
Bu, @ gniourf_gniourf'un geç cevabına daha da sonra gelen bir cevaptır, ki bu sadece en iyi cevaptır çünkü iki kez bitti. (Bir kez kaçınmak evalve bir kez güvenli dosya adı işlemek için.)
Ancak bu cevabın kullandığı "çok iyi belgelenmemiş" özellik (ler) i çözmek birkaç dakika sürdü. Bash becerileriniz nasıl çalıştığını hemen görebilecek kadar sağlamsa, bu yorumu atlayın. Ama ben yapmadım ve çözdüğümde açıklamaya değer olduğunu düşünüyorum.
Özellik # 1 , kabuğun kendi dosya globbingidir. üyeleri geçerli dizindeki dosyalar olan a=(*)bir dizi oluşturur $a. Bash, dosya adlarının tüm tuhaflıklarını anlar, böylece liste doğru, garantili kaçış vb. Garanti edilir ls. Döndürülen metin dosyası adlarını düzgün ayrıştırmak konusunda endişelenmenize gerek yoktur .
Özellik # 2 , biri diğerinin içine yerleştirilmiş diziler için Bash parametresi genişletmeleridir . Bu , uzunluğu ile genişleyen ile başlar .${#ARRAY[@]}$ARRAY
Bu genişletme daha sonra diziye abone olmak için kullanılır. 1 ve N arasında rastgele bir sayı bulmanın standart yolu, rastgele sayı modulo N değerini almaktır. 0 ile dizimizin uzunluğu arasında rastgele bir sayı istiyoruz. İşte netlik için iki çizgiye ayrılmış yaklaşım:
LENGTH=${#ARRAY[@]}
RANDOM=${a[RANDOM%$LENGTH]}
Ancak bu çözüm, gereksiz değişken atamasını kaldırarak tek bir satırda yapar.
Özellik # 3 , Bash brace genişlemesi , ancak itiraf etmeliyim, ancak tamamen anlamıyorum. Ayraç genişleme adında 25 dosyaların bir listesini oluşturmak için, örneğin, kullanılan filename1.txt, filename2.txtvb: echo "filename"{1..25}".txt".
Yukarıdaki alt kabuk içindeki ifade, "${a[RANDOM%${#a[@]}]"{1..42}"}"42 ayrı genişletme üretmek için bu numarayı kullanır. Küme ayracı genişletme , ilk önce diziye abone olduğunu düşündüğüm ]ve arasında tek bir basamak yerleştirir }, ancak öyleyse önünde bir iki nokta üst üste olur. (Aynı zamanda dizideki 42 rastgele öğeyi döndürmekle aynı şey olmayan, dizideki rastgele bir noktadan arka arkaya 42 öğe döndürürdü.) Bence sadece kabuk genişletmeyi 42 kez çalıştırıyor, böylece geri dönüyor Diziden 42 rastgele öğe. (Ama birisi daha eksiksiz açıklayabilirse, duymak isterim.)
N'nin kodlanmasının (42'ye kadar) nedeni, küme genişlemesinin değişken genişlemeden önce gerçekleşmesidir.
Son olarak, bir dizin hiyerarşisi için bunu tekrar tekrar yapmak istiyorsanız Özellik # 4'ü aşağıda bulabilirsiniz :
shopt -s globstar
a=( ** )
Bu, özyinelemeli olarak eşleşmesine neden olan bir kabuk seçeneğini açar **. Şimdi $adiziniz tüm hiyerarşideki her dosyayı içeriyor.
Klasörünüzde daha fazla dosya varsa, unix stackexchange'te bulduğum aşağıdaki borulu komutu kullanabilirsiniz .
find /some/dir/ -type f -print0 | xargs -0 shuf -e -n 8 -z | xargs -0 cp -vt /target/dir/
Burada dosyaları kopyalamak istedim, ancak dosyaları taşımak veya başka bir şey yapmak istiyorsanız, kullandığım son komutu değiştirin cp.
MacOS'ta bash ile güzel oynayabileceğim tek komut dosyası bu. Snippet'leri aşağıdaki iki bağlantıdan birleştirdim ve düzenledim:
ls komut: özyinelemeli tam yol listesi, dosya başına bir satır nasıl alabilirim?
#!/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
MacOS sıralama -R ve shuf komutları yok, bu yüzden tüm dosyaları çoğaltmadan rastgele bir bash çözümüne ihtiyacım vardı ve burada bulamadık gerekiyordu. Bu çözüm gniourf_gniourf'un # 4 numaralı çözümüne benzer, ancak umarım daha iyi yorumlar ekler.
Komut dosyası, if ile bir sayaç kullanarak N örnekleri sonra durdurmak için kolayca değiştirilebilmelidir veya N ile döngü için gniourf_gniourf '$ $ RANDOM ~ 32000 dosya ile sınırlıdır, ancak bu çoğu durumda yapılmalıdır.
#!/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
Bunu kullanıyorum: geçici dosya kullanıyor, ancak normal bir dosya bulana ve geri dönene kadar bir dizinde derinlemesine gidiyor.
# 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;
Bay Kang'dan hafifçe doktorluk yapan bir Perl çözümüne ne dersiniz:
Unix komut satırında veya bir kabuk komut dosyasında bir metin dosyasının satırlarını nasıl karıştırabilirim?
$ ls | perl -MList :: Util = karıştır -e '@ satırları = karıştır (<>); print @lines [0..4] '