1つのループで2つのシーケンスを実行する


8

以下のように、シェルの同じループで2つのシーケンスを実行しようとしています。

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

どうすればこれを達成できますか?


@zanna-私の最初の考えは、ブール値の「and」は排他的であるということです。つまり、結果は両方のセットに存在する数値になるということです。この場合はどれもありません。包括的な「and」はありますか?
ravery

1
@ravery私が探しているものを説明するために「と」だけを入れました
HISI

2
@YassineSihi-よく注意してください。多くの新しいプログラマーは、話し言葉は「and」を包括的に使用しますが、論理的な「and」はほとんどのプログラミング言語で排他的であるため、脳を再訓練できるようになるまでこの点でつまずきます。
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
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy彼らは実際にechoは数字だけを望んでいるのではないと思います
Zanna

うん、touchファイルのような何らかのアクションがtouch {1..15}.txt {20..25}.txt必要な場合は、実行するだけでよく、ここではループは必要ありません。しかし、もちろん、それが同じ数の複数のアクションである場合-わかりました、それはループを使用することができます。
Sergiy Kolodyazhnyy 2017年

6

別の方法として、seq一連の数値を出力する)を使用することもできます。以下に2つの同等の例を示します。

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

Zannaの回答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 2017年

結構 !ハッピースクリプト!
Sergiy Kolodyazhnyy

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

もちろん、このソリューションはより冗長ですが、短縮できるものもありますが、機能します。でテストされkshdashmksh、そしてもちろんbash


Bash Cスタイルのループ

しかし、ループをbash固有にしたい場合は(何らかの理由で、おそらく印刷だけでなく、それらの数値を使用して何かを実行する場合も)、これも実行できます(基本的には、ポータブルソリューションの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} `を使用することもできます。物事を行うには多くの方法があり、それはスクリプトをとても楽しいものにします!xargstouch
Sergiy Kolodyazhnyy 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.