Як я можу зробити сценарій для підрахунку до п'яти?


10

Я намагався зробити дуже простий сценарій bash, щоб перелічити всі кратні п’ять між 375 і 3500 (375, 380, 385 ...). Одне, що я намагався і не спрацював, це:

for i in {375..3500}
do
        echo $i
        (($i += 5))
done

Через деякий час я відмовився і написав це в BASIC приблизно за 15 секунд:

10 count = 375
20 print count
30 count = count+5
40 if count < 3500 then goto 20

Як я можу створити свою програму BASIC в баш-скрипті?


це краще при переповненні стека - Unix & Linux стосується ОС, а не кодування. Кодування призначене для stackoverflow.
noɥʇʎԀʎzɐɹƆ

Відповіді:


20

Як варіант, можна використовувати традиційний C-стиль для циклу:

for ((i=375; i<=3500; i+=5)); do
    echo $i
done

Це, можливо, менш зрозуміло, ніж використання послідовності, але це не породить жодних підпроцесів. Хоча, оскільки я знайомий із C, я не мав би жодних труднощів із розумінням цього, але YMMV.


1
Недоліком цього методу є те, що він менш портативний. Метод seq працює в POSIX sh.
Wouter Verhelst

О, хіба в POSIX sh не існує стилю C для циклів? Ти щодня дізнаєшся щось нове ...
Muzer

2
@WouterVerhelst Ну, за винятком випадків, коли ви обмежені POSIX sh, ви, швидше за все, не матимете seq, який є частиною GNU coreutils. busbox має більш обмежену реалізацію, але поза цим багато вбудованих систем, швидше за все, не матимуть цього.
Кріс Даун

1
@Muzer - принаймні, dashце не підтримує. @ChrisDown - Там немає причин , чому «POSIX ш» має не має на увазі «ні SEQ». Але так, на жаль, я не помітив того, що seq не визначений POSIX.
Wouter Verhelst

27

Оскільки ви все одно використовуєте розширення дужок, то використовуйте його функцію повністю:

echo {375..3500..5}

Ви також можете використовувати цю техніку для друку кожного номера в окремому рядку з додатковим текстом, використовуючи printfзамість echo, наприклад:

$ printf "Number %s is generated.\n" {375..3500..5}
Number 375 is generated.
Number 380 is generated.
Number 385 is generated.
...

Редагувати

Як вказував @kojiro в коментарі, Mac OS використовує bash 3 як оболонку за замовчуванням, яка не підтримує збільшення в послідовності вираження дужки розширення. Вам потрібно оновити до bash версії 4 або використовувати іншу оболонку, яка підтримує це (наприклад, нещодавній zsh).


2
Це не працює для мене на Mac OS 10.10.3. Він видає "{375..3500..5}".
aswine

6
@aswine, тому завжди потрібно згадувати вашу ОС, коли запитуєте. Тим більше, що OSX має ряд відмінностей як від інших UNICE, так і від Linux.
terdon

2
Сама по собі це не різниця в ОС. Це різниця версій. У OS X є лише BASH 3 OOTB. Ви можете встановити BASH з цього століття, використовуючи практично будь-який менеджер пакунків OS X. Я використовую Home Brew, для одного.
kojiro

2
Той факт, що розширення не спрацьовує безпосередньо, але вдається в bash -cзначній мірі, гарантує залучення двох різних оболонок. Каже $SHELL --version, це баш 3?
даг

3
@mikeserv Відповідь дуже проста - я не писав цього, оскільки не мав уявлення, в якій версії bashцієї функції було введено. Я сам це довідався з коментаря до kamro. І, будь ласка, не порівнюйте мене з SC, я дійсно не знаю всіх подробиць та історії всіх снарядів з кінця 1970 року. Ще раз - якщо ви цього очікуєте, просто будь ласка, зверніть увагу.
jimmij

17

Використання SEQ (1)

for i in $(seq 375 5 3500)
do
    echo $i
done

Або просто:

seq 375 5 3500

4
Ще простіше було б скинути цикл і просто використовувати seq 375 5 3500.
dhag

11

Ваш фрагмент for-loop не працює так, як вам потрібно з двох причин:

  • (($i += 5))- тут $iрозширений до значення i. Таким чином, розширення буде чимось подібним ((375 += 5)), що не має сенсу (спроба присвоїти буквальне число іншому буквальному числу). Зазвичай це досягається за допомогою ((i += 5))(не $для розширення змінної)
  • Захист {375..3500}буде розширено до першої ітерації циклу. Це буде список номерів 375 376 ... 3499 3500. Для кожної ітерації циклу iбуде присвоєно кожному з цих чисел по одному. Таким чином, на початку кожної ітерації iбуде перепризначено наступне значення у цьому списку, підраховуючи кроки 1. ((i += 5))Ефективно нічого не робить - додає 5 до i, але тоді я просто перепризначається знову на початку наступна ітерація.

Я думаю, що for (( ; ; ))відповідь мені найбільше подобається , але ось кілька варіантів, щоб змусити вас задуматися:


Оскільки ми маємо справу з кратними 5, а {a..b..i}розширення не підтримується у версії bash 3.2.57 (1) (в OS X), то натомість ми можемо зробити це злегка таємничо:

for i in 375 {38..349}{0,5} 3500; do
    echo $i
done

Це демонструє, як баш можна використовувати для створення декартового продукту.


Я думаю, що в цілому a для циклу - це найзручніший спосіб зробити це, але якщо вам цікаво, ви можете скористатися програмою while-loop (трохи ближче до вашої ОСНОВНОЇ):

count=375
while (( count <= 3500 )); do
    echo $count
    (( count += 5 ))
done

1
@jimmij ... що таке декартовий продукт? Ви можете оскаржити коментар, якщо хочете, і мені навіть буде байдуже, якщо ви мені скажете. Мені цікаво.
mikeserv

1
@mikeserv ви не можете оскаржити коментар: en.wikipedia.org/wiki/Cartesian_product
jimmij

@jimmij - зі мною все одно гаразд.
mikeserv

@jimmij Чорт ти говориш? Декартовий виріб - це набір кортежів усіх можливих поєднань елементів із двох наборів (які можуть бути, а можуть і не бути однаковими). Більш розмовно, це просто "всі можливі комбінації" з двох груп речей. Я бачу лише один набір. Як тут є декартовий продукт?
jpmc26

1
@ jpmc26 Ви маєте рацію, це "всі можливі комбінації", тому давайте накресліть дві осі - спочатку запишіть усі числа від 38до 349. На другому лише два числа: 0і 5. Тепер давайте створювати впорядковані пари цих всіх комбінацій - ви можете написати їх як набори: {38,0}, {38,5}... {349,5}, або просто видалити зайві символи {, ,а }й отримати нові номери ... 380... 3495.
jimmij

5

Хоча, звичайно, є додаток для цього ( seq 375 5 3500), є різні способи зробити це з командного рядка. Хоча найшвидшим і найпростішим буде просто використання seq, ось деякі інші варіанти:

for i in {375..3500}; do [[ (($i % 5)) -eq 0 ]] && echo $i; done

i=370; while [ $i -le 3500 ]; do printf "%s\n" $((i+=5)); done

perl -le '$i=shift;while($i<=$ARGV[0]){print $i; $i+=5; }' 375 3500
perl -le 'map{print $_ + 5} 370..3495'

awk 'BEGIN{ for(i=375;i<=3500;i+=5){print i}}'

Nit pick: $(($i % 5))можна писати $((i % 5)).
G-Man каже: "Відновіть Моніку"

4

POSIXly:

i=370
while [ 3500 -gt "$i" ]
do    echo "$((i+=5))"
done

... або ...

echo 'for(x=370;x<=3500;x+=5)x' |bc

Я не знаю, чому ви це зробите будь-яким іншим способом. За винятком, звичайно ...

seq 375 5 3500

... або з dc:

echo '370[5+pd3500>p]splpx'|dc

Мені цікаво, як dcпрацює код. Це, звичайно, більш інтригуюче, ніж використання seq.
dhag

1
@dhag - він пише невеликий цикл. він sпередає [рядок ]у верхню частину pмасиву, потім lповертає його назад у верхню частину стека та xвиконує його як макрос. У макросі ми натискаємо 5на стек, потім виводимо його, а другий зверху та +їх, замінюючи обидва значення стека їх сумою, яка передзвонюється pта dпосилюється до того, як 3500буде натиснута на стек, коли ми з'явимо його та дуп, який ми тільки що зробили для порівняння >. Якщо 3500більше, ми завантажуємо і виконуємо як макрос рядок, що зберігається в pмасиві (наш поточний макрос) , або якщо не розривається.
mikeserv

3

Якщо ви застрягли на Bash 3:

echo {375..3500} | tr ' ' '\n' | sed -n 'p;n;n;n;n'

і якщо ви віддаєте перевагу awk:

echo {375..3500} | tr ' ' '\n' | awk '!((NR-1)%5)'

Я не знав про розширення брекетів - це дуже серйозно.

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