Як запустити команду 1 з N разів у Bash


15

Я хочу спосіб запускати команду випадковим чином, скажімо, 1 з 10 разів. Чи є вбудований або GNU coreutil для цього, в ідеалі щось на кшталт:

chance 10 && do_stuff

де do_stuffвиконується лише 1 в 10 разів? Я знаю, що міг би написати сценарій, але це здається досить простою справою, і мені було цікаво, чи є визначений спосіб.


1
Це досить хороший показник того, що ваш сценарій, ймовірно, надто виходить з-за руки, щоб Bash і надалі був розумним вибором. Вам слід розглянути більш повноцінну мову програмування, можливо, мову сценаріїв на зразок Python або Ruby.
Олександр -

@Alexander це навіть не зовсім сценарій, а лише один рядок. Я використовую це в роботі з крон, щоб раз у раз повідомляти мене випадковим чином як нагадування про щось
retnikt

Відповіді:


40

В ksh, Bash, Zsh, Yash або BusyBox sh:

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

RANDOMСпеціальна змінна Корн, Bash, Яш, Z і BusyBox оболонок виробляє псевдовипадкове десяткове ціле число в діапазоні від 0 до 32767 кожен раз , коли вона оцінена, так що вище , дає (близько до) на один з десяти випадково.

Ви можете використовувати це для створення функції, яка поводиться так, як описано у вашому запитанні, принаймні в Bash:

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

Забувши надати аргумент або надати недійсний аргумент, це дасть результат 1, тому chance && do_stuffніколи do_stuff.

Для цього використовується загальна формула для "1 в n ", використовуючи $RANDOM, що [[ $RANDOM -lt $((32767 / n + 1)) ]]дає шанс (⎣32767 / n ⎦ + 1) у 32768 шанс. Значення nяких не є коефіцієнтами 32768, вводять зміщення через нерівномірний розкол в діапазоні можливих значень.


(1/2) Повторне відвідування цього питання: Інтуїтивно зрозуміло, що має бути верхня межа кількості деталей, наприклад, не може бути 40 000 частин. Насправді реальна межа досить менша. Проблема полягає в тому, що ціле ділення є лише наближенням до того, якою великою може бути кожна частина. Потрібно, щоб дві послідовні частини призводили до того, що різниця межі $((32767/parts+1))буде більшою за 1, або ми ризикуємо збільшити кількість частин на 1, тоді як результат поділу (і, отже, межа) буде однаковим. (продовж ..)
Ісаак

(2/2) (продовження). Це враховує більше чисел, ніж насправді є. Формула для цього полягає у (32767/n-32767/(n+1))>=1розв’язуванні для n, що дає межу при ~ 181,5. Насправді кількість деталей може пропрацювати до 194 без випуску. Але на 195 частинах отримана межа - 151, такий же результат, як і у 194 частин. Це є невідповідним і цього слід уникати. Коротше кажучи, верхня межа кількості деталей (n) повинна бути 194. Ви можете зробити граничні тести:[[ -z $1 || $1 -le 1 || $1 -ge 194 ]] && return 1
Ісаак

25

Нестандартне рішення:

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

Перевірте, чи остання цифра поточного часу в наносекундах дорівнює 1!


Це круто.
Ерік Думініл

2
Ви повинні переконатися, що дзвінок [ $(date +%1N) == 1 ] && do_stuffне відбувається через регулярні проміжки часу, інакше випадковість зіпсується. Розглядайте while true; do [ $(date +1%N) == 1 ] && sleep 1; doneяк абстрактний контрприклад. Тим не менше, ідея гри з наносекундами справді гарна, я думаю, я використаю її, отже +1
XavierStuvw

3
@XavierStuvw Я думаю, ви мали б бажання, якби код перевіряв секунди. Але наносекунд? Це повинно виглядати справді випадково.
Ерік Думініл

9
@EricDuminil: На жаль: 1) Системний годинник не зобов'язаний за контрактом надати фактичну наносекундну роздільну здатність (він може округлитись до найближчого clock_getres()), 2) Планувальнику не забороняється, наприклад, завжди запускати часовий відрізок на межі 100 наносекунд. (що може ввести зміщення, навіть якщо годинник правильний) 3) Підтримуваний спосіб отримання випадкових значень - це (як правило) зчитування /dev/urandomчи перевірка значення $RANDOM.
Кевін

1
@Kevin: Дякую за коментар.
Ерік Думініл

21

Альтернативою для використання $RANDOMє shufкоманда:

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

зробить роботу. Також корисно для випадкового вибору рядків з файлу, наприклад. для музичного списку відтворення.


1
Не знав цієї команди. Дуже хороша!
Eisenknurr

12

Удосконалення за першою відповіддю та надання більш очевидного того, що ви намагаєтесь досягти:

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

1
$RANDOM % 10буде мати упередження, якщо тільки не видасть $RANDOMточно кратне 10 різних значень (що, як правило, не відбувається у двійковому комп'ютері)
phuclv

1
@phuclv Так, він матиме такий самий ухил, що і "$RANDOM" -lt $((32767 / n + 1)).
Айзенкнурр

Так, ухил однаковий, 0,100006104 замість 0,1 для 1-в-10 (при перевірці на 0).
Стівен Кітт

1

Я не впевнений, чи хочете ви випадковості чи періодичності ... Для періодичності:

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

Ви можете змішати його з трюком "$ RANDOM" вище, щоб створити щось більш хаотичне, наприклад:

за я в seq 1 1000 $RANDOM; робити відлуння $ i; зроблено

HTH :-)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.