Yaklaşık 2000 dosya içeren bir dizin var. N
Bir 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. N
Bir 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=COUNT
satı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
N
rastgele dosyaları seçmek istedi , bu yüzden kullanmak 1
biraz yanıltıcı.
find dirname -type f -print0 | shuf -zn1
İşte çıktılarını ayrıştırmayan ve ls
adlarında boşluk ve komik sembollere sahip dosyalar için% 100 güvenli olan birkaç olasılık . Hepsi bir dizi randf
rastgele 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 N
kontrol 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 eval
ve 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[@]}]}" )
done
Aynı 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}" )
done
Not . 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, $RANDOM
sadece 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.
ls
size "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.
ls
dizinler ve boş satırlar içerebilir. Bunun find . -type f | shuf -n10
yerine 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
echo
Dosyalarınız için yürütmek istediğiniz komutla değiştirin .
read
ayrıştırma ile aynı sorunlara sahip değil ls
mi? 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())"
N
Dosyaları / satırları seçmek için şunu kullanın (not N
komutun 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 eval
ve 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.txt
vb: 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 $a
diziniz 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] '