Cách chạy lệnh 1 trong số N lần trong Bash


15

Tôi muốn một cách để chạy một lệnh ngẫu nhiên, nói 1 trên 10 lần. Có lõi dựng sẵn hoặc GNU để thực hiện việc này không, lý tưởng là:

chance 10 && do_stuff

nơi do_stuffchỉ được thực hiện 1 trong 10 lần? Tôi biết tôi có thể viết một kịch bản, nhưng nó có vẻ như là một điều khá đơn giản và tôi đã tự hỏi nếu có một cách xác định.


1
Đây là một chỉ báo khá tốt cho thấy kịch bản của bạn có thể trở nên quá tầm với để bash tiếp tục là một lựa chọn hợp lý. Bạn nên xem xét một ngôn ngữ lập trình đầy đủ hơn, có thể là ngôn ngữ kịch bản như Python hoặc Ruby.
Alexander - Tái lập Monica

@Alexander đây thậm chí không thực sự là một kịch bản, chỉ là một dòng. Tôi đang sử dụng nó trong một công việc định kỳ để thông báo cho tôi ngẫu nhiên mọi lúc và sau đó như một lời nhắc nhở để làm một cái gì đó
retnikt

Câu trả lời:


40

Trong ksh, Bash, Zsh, Yash hoặc BusyBox sh:

[ "$RANDOM" -lt 3277 ] && do_stuff

Biến RANDOMđặc biệt của các vỏ Korn, Bash, Yash, Z và BusyBox tạo ra giá trị số nguyên thập phân giả ngẫu nhiên trong khoảng từ 0 đến 32767 mỗi khi nó được đánh giá, do đó, ở trên có cơ hội một phần mười.

Bạn có thể sử dụng điều này để tạo ra một hàm hoạt động như được mô tả trong câu hỏi của bạn, ít nhất là trong Bash:

function chance {
  [[ -z $1 || $1 -le 0 ]] && return 1
  [[ $RANDOM -lt $((32767 / $1 + 1)) ]]
}

Việc quên cung cấp một đối số hoặc cung cấp một đối số không hợp lệ sẽ tạo ra kết quả là 1, vì vậy chance && do_stuffsẽ không bao giờ do_stuff.

Này sử dụng công thức chung cho “1 trong n ” sử dụng $RANDOM, đó là [[ $RANDOM -lt $((32767 / n + 1)) ]], đưa ra một (⎣32767 / n ⎦ + 1) trong 32.768 cơ hội. Các giá trị nkhông phải là yếu tố của 32768 đưa ra sai lệch do sự phân chia không đồng đều trong phạm vi của các giá trị có thể.


(1/2) Khi truy cập lại vấn đề này: Ví dụ trực quan là phải có giới hạn trên về số lượng bộ phận, ví dụ như không thể có 40.000 bộ phận. Trên thực tế giới hạn thực sự là khá nhỏ. Vấn đề là sự phân chia số nguyên chỉ là một xấp xỉ với mức độ lớn của mỗi phần. Yêu cầu hai phần liên tiếp dẫn đến chênh lệch giới hạn $((32767/parts+1))lớn hơn 1 hoặc chúng ta có nguy cơ số lượng phần tăng lên 1 trong khi kết quả của phép chia (và do đó giới hạn) là như nhau. (tiếp tục ..)
Isaac

(2/2) (tiếp ..) Điều đó sẽ chiếm số lượng nhiều hơn số thực tế có sẵn. Công thức cho điều đó là (32767/n-32767/(n+1))>=1, giải cho n đưa ra giới hạn ở mức ~ 181,5. Trong thực tế, số lượng các bộ phận có thể chạy lên đến 194 mà không có vấn đề. Nhưng ở 195 phần, giới hạn kết quả là 151, kết quả tương tự với 194 phần. Điều đó là không phù hợp và nên tránh. Nói tóm lại, giới hạn trên cho số phần (n), phải là 194. Bạn có thể thực hiện các bài kiểm tra giới hạn:[[ -z $1 || $1 -le 1 || $1 -ge 194 ]] && return 1
Isaac

25

Giải pháp không chuẩn:

[ $(date +%1N) == 1 ] && do_stuff

Kiểm tra xem chữ số cuối cùng của thời gian hiện tại tính bằng nano giây là 1!


Thật tuyệt vời.
Eric Duminil

2
Bạn phải chắc chắn rằng cuộc gọi [ $(date +%1N) == 1 ] && do_stuffkhông xảy ra đều đặn, nếu không thì sự ngẫu nhiên bị phá hỏng. Hãy nghĩ về while true; do [ $(date +1%N) == 1 ] && sleep 1; donemột ví dụ trừu tượng. Tuy nhiên, ý tưởng chơi với nano giây thực sự tốt, tôi nghĩ rằng tôi sẽ sử dụng nó, do đó +1
XavierStuvw

3
@XavierStuvw Tôi nghĩ rằng bạn sẽ có điểm nếu mã đang kiểm tra giây. Nhưng nano giây? Nó sẽ xuất hiện thực sự ngẫu nhiên.
Eric Duminil

9
@EricDuminil: Thật không may: 1) Đồng hồ hệ thống không bắt buộc phải cung cấp độ phân giải nano giây thực tế (có thể làm tròn đến gần nhất clock_getres()), 2) Ví dụ, bộ lập lịch không bị cấm, luôn luôn bắt đầu một khoảng thời gian trên ranh giới 100 nano giây (sẽ giới thiệu sai lệch ngay cả khi đồng hồ đúng), 3) Cách được hỗ trợ để có được các giá trị ngẫu nhiên là (thường) đọc từ /dev/urandomhoặc kiểm tra giá trị của $RANDOM.
Kevin

1
@Kevin: Cảm ơn rất nhiều vì đã bình luận.
Eric Duminil

21

Một thay thế cho việc sử dụng $RANDOMshuflệnh:

[[ $(shuf -i 1-10 -n 1) == 1 ]] && do_stuff

sẽ làm việc Cũng hữu ích cho việc chọn ngẫu nhiên các dòng từ một tệp, ví dụ. cho một danh sách nhạc


1
Không biết lệnh đó. Rất đẹp!
Eisenknurr

12

Cải thiện câu trả lời đầu tiên và làm cho nó rõ ràng hơn nhiều những gì bạn đang cố gắng đạt được:

[ $(( $RANDOM % 10 )) == 0 ] && echo "You win" || echo "You lose"

1
$RANDOM % 10sẽ có thành kiến, trừ khi $RANDOMsản xuất chính xác bội số của 10 giá trị khác nhau (thường không xảy ra trong máy tính nhị phân)
phuclv

1
@phuclv Có, nó sẽ có cùng xu hướng như "$RANDOM" -lt $((32767 / n + 1)).
Eisenknurr

Có, độ lệch là giống hệt nhau, 0.100006104 thay vì 0.1 cho 1 trong 10 (khi kiểm tra so với 0).
Stephen Kitt

1

Tôi không chắc chắn nếu bạn muốn tính ngẫu nhiên hay định kỳ ... Đối với tính định kỳ:

for i in `seq 1 10 100`; do echo $i;done
1
11
21
31
41
51
61
71
81
91

Bạn có thể trộn nó với thủ thuật "$ RANDOM" ở trên để tạo ra thứ gì đó hỗn loạn hơn, ví dụ:

cho tôi vào seq 1 1000 $RANDOM ; làm vang $ i; xong

HTH :-)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.