Проведіть через дві послідовності в одну петлю


8

Я намагаюся пропустити дві послідовності в одному циклі в оболонці, як нижче:

#!/bin/bash
for i in (1..15) and (20..25) ;
do
     echo $i
     ......
     .....other process
done

будь-яка ідея, як я можу цього досягти?


@zanna - моя перша думка полягає в тому, що булеві "і" є ексклюзивними, це означає, що результат - це числа, які існують на обох множинах; чого в цьому випадку немає. Чи є включно "і"?
ravery

1
@ravery Я поставив "і" просто щоб пояснити, що я шукаю
HISI

2
@YassineSihi - добре зауважте. Багато нових програмістів стикаються з цим питанням, поки не зможуть перевчити свій мозок, оскільки розмовна мова використовує "та" включно, але логічне "та" є ексклюзивним для більшості мов програмування.
ravery

Відповіді:


10

Для цього вам потрібно лише розширення дужок

$ for n in {1..3} {200..203}; do echo $n; done
1
2
3
200
201
202
203

Ми можемо передати список for( ).for i in x y z; do stuff "$i"; done

Тож ось, брекети { }отримують оболонку, щоб розширити ваші послідовності до списку. Вам потрібно лише поставити пробіл між ними, оскільки оболонка розбиває списки аргументів на них.


Так, брекети. . . І вам навіть не потрібна петля для цього ^ _0
Сергій Колодяжний

@SergiyKolodyazhnyy Я вважаю, що вони насправді не хочуть просто echoцифри
Zanna

так, якщо вони хочуть певних дій, як touchфайли, вони можуть просто зробити touch {1..15}.txt {20..25}.txt, тут не потрібна петля. Але звичайно, якщо це кілька дій на одне число - ОК, це може використовувати цикл.
Сергій Колодяжний

6

Можна також використати seq( друкувати послідовність чисел ), ось два еквівалентні приклади:

for i in `seq 1 3` `seq 101 103`; do echo $i; done
for i in $(seq 1 3) $(seq 101 103); do echo $i; done

Якщо це сценарій, для повторюваних завдань можна використовувати функції:

#!/bin/bash
my_function() { echo "$1"; }
for i in {1..3}; do my_function "$i"; done
for i in {101..103}; do my_function "$i"; done
#!/bin/bash
my_function() { for i in `seq $1 $2`; do echo "$i"; done; }
my_function "1" "3"
my_function "101" "103"

4

Відповідь Žanna в і відповідь pa4080 ігрових обидва гарні , і я б , ймовірно , піти з одним з них у більшості випадків. Можливо, це само собою зрозуміло, але задля повноти я все одно скажу: Ви можете завантажити кожне значення в масив, а потім перевести цикл на масив. Наприклад:

the_array=( 1 2 3 4 5 6 7 8 9 10 20 21 22 23 24 25 )
for i in "${the_array[@]}";
do
    echo $i
done

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

Дуже добре ! Щасливі сценарії!
Сергій Колодяжний

3

Петля без петлі

Відповідь Zanna абсолютно правильна і добре підходить для bash, але ми можемо вдосконалити це ще більше, не використовуючи цикл.

printf "%d\n"  {1..15} {20..25}

Поведінка printfтакої, що якщо кількість значень ARGUMENTSбільше, ніж контроль у форматі 'FORMAT STRING', то printfвони розділять усі ARGUMENTS на рівні фрагменти і продовжуватимуть прилаштовувати їх до рядка формату знову і знову, поки не закінчується ARGUMENTSсписок.

Якщо ми прагнемо портативності, ми можемо використовувати це printf "%d\n" $(seq 1 15) $(seq 20 25)замість цього

Візьмемо це далі і веселіше. Скажімо, ми хочемо виконати дію, а не просто друкувати номери. Створення файлів із цієї послідовності чисел ми могли легко зробити touch {1..15}.txt {20..25}.txt. Що робити, якщо ми хочемо, щоб відбулося багато речей? Ми також могли б зробити щось подібне:

$ printf "%d\n" {1..15} {20..25} | xargs -I % bash -c 'touch "$1.txt"; stat "$1.txt"' sh %

Або якщо ми хочемо зробити його старошкільним стилем:

printf "%d\n" {1..15} {20..25} | while read -r line; do 
    touch "$line".txt;
    stat "$line".txt;
    rm "$line".txt; 
done

Портативна, але багатослівна альтернатива

Якщо ми хочемо зробити сценарій рішення, яке працює з оболонками, які не мають розширення дужок (на що {1..15} {20..25}покладається), ми можемо написати простий цикл while:

#!/bin/sh
start=$1
jump=$2
new_start=$3
end=$4

i=$start
while [ $i -le $jump ]
do
    printf "%d\n" "$i"
    i=$((i+1))
    if [ $i -eq $jump ] && ! [ $i -eq $end ];then
        printf "%d\n" "$i"
        i=$new_start
        jump=$end
    fi
done

Звичайно, це рішення є більш багатослівним, деякі речі можна скоротити, але воно працює. Випробувано з ksh, dash, mkshі, звичайно ж bash.


Петля в стилі Bash C

Але якщо ми хотіли зробити певну певну певну петлю (з будь-якої причини, можливо, не просто друкуючи, а й роблячи щось із цими номерами), ми також можемо це зробити (в основному версія портативного рішення на C-циклі):

last=15; for (( i=1; i<=last;i++ )); do printf "%d\n" "$i"; [[ $i -eq $last ]] && !  [[ $i -eq 25 ]] && { i=19;last=25;} ;done

Або в більш читаному форматі:

last=15
for (( i=1; i<=last;i++ )); 
do 
    printf "%d\n" "$i"
    [[ $i -eq $last ]] && !  [[ $i -eq 25 ]] && { i=19;last=25;} 
done

Порівняння продуктивності різних циклічних підходів

bash-4.3$ time bash -c 'printf "%d\n" {0..50000}>/dev/null'

real    0m0.196s
user    0m0.124s
sys 0m0.028s
bash-4.3$ time bash -c 'for i in {1..50000}; do echo $i > /dev/null; done'

real    0m1.819s
user    0m1.328s
sys 0m0.476s
bash-4.3$ time bash -c ' i=0;while [ $i -le 50000 ]; do echo $i>/dev/null; i=$((i+1)); done'

real    0m3.069s
user    0m2.544s
sys 0m0.500s
bash-4.3$ time bash -c 'for i in $(seq 1 50000); do printf "%d\n" > /dev/null; done'

real    0m1.879s
user    0m1.344s
sys 0m0.520s

Альтернативна альтернатива

Просто тому, що ми можемо ось рішення Python

$ python3 -c 'print("\n".join([str(i) for i in (*range(1,16),*range(20,26))]))'

Або з трохи шкаралупи:

bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
>    print(i)
> EOF

1
Я щойно тестував touch $(printf "%d\n" {1..15} {20..25}):-)
pa4080

1
@ pa4080 насправді bashвам $()там навіть не потрібно , просто touch {1..15}.txt {20..25}.txt :) Але, звичайно, ми могли б використовувати printf "%d\n{1..15} {20..25} `, xargsякщо б хотіли зробити більше, ніж просто touchфайли. Є багато способів зробити речі, і це робить сценарій так веселим!
Сергій Колодяжний
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.