私はスクリプトに初めて触れ、助けが必要です。答えてくれてありがとう。
私は、次の数字のグループのうちの2つを含む5桁の数字(10000 - 99999の範囲)の合計を求める任務を受けました:{4、5、6}。これは同じ回数内で繰り返すことができ、その場合は各発生は一度計算されます。
一致する数値の例には、42057、74638、および89515があります。私はこの小さなコードを持っています。
#! /bin/bash
for (( CON1=10000; CON1<=99999; CON1++ )) ;
do
## UNKNOWN COMMANDS
done
答え1
以下は、数字に4、5、6がいくつ出ているかを計算し、bash
結果が2であるかどうかに基づいてステートメントを実行する1つの方法です。
$ con1=1457
$ a=${con1//[^456]/}; [ ${#a} -eq 2 ] && echo Yes
Yes
答え2
始める
私はこのようなプロジェクトがあるたびに段階的に進むのが好きです。私が最初にやりたいことは、echo
ループ内に追加して実行して、ループが私が望むものを提供していることを確認することです。
#! /bin/bash
for (( CON1=10000; CON1<=99999; CON1++ )) ;
do
echo $CON1
done
これで実行すると、head -5
出力される最初の5行だけが表示されます。
$ ./cmd.bash | head -5
10000
10001
10002
10003
10004
いいですね。次のようにエンディングを確認してください。
$ ./cmd.bash | tail -5
99995
99996
99997
99998
99999
よさそうだねここで、{4,5,6} セットで 2 桁の数字を識別する次の手順を実行するいくつかの方法を見てみましょう。私の最初の本能はそれを見つけることでしたgrep
。 Bashでのみこれを行う方法がありますが、私はさまざまなツール、、、そしてgrep
これを行うのが好きです。主にそう思うからです。awk
sed
離れた
grep
それでは、{4,5,6}セットで2桁の数字を含む行をどのように見つけることができますか?これを行うには、正規表現として次のように書かれた集合表記法を使用できます[456]
。セットで一致させる桁数を指定することもできます。次のように書いてください。
[456]{#}
#
数字または数字の範囲はどこにありますか? 3つが欲しいと書いて、[456]{3}
2~5桁がほしいと書いています[456]{2,5}
。 3つ以上が欲しいなら[456]{3,}`と書いてください。
したがって、あなたのシナリオではです[456]{2}
。で正規表現を使用するには、特定のgrep
バージョンがそのスイッチをサポートするgrep
必要があります-E
。これは通常、ほとんどの標準で使用できますgrep
。
$ echo "45123" | grep -E "[456]{2}"
45123
動作しているように見えますが、数字3を指定すると問題が発生し始めます。
$ echo "45423" | grep -E "[456]{2}"
45423
これもよく当てはまります。grep
これは、文字列に数字という概念がないためです。これは愚かなことです。文字列の一連の文字がセットからのもので、文字列に2つの文字と2つの数字があるかどうかを知らせるように指示します45423
。
次の文字列も失敗します。
$ echo "41412" | grep -E "[456]{2}"
$
それでは、この方法はうまくいきますか?戦略を少し変更するだけで終わりですが、正規表現を再調整する必要があります。
はい
$ echo -e "41123\n44123\n44423\n41423" | grep -E "[^456]*([456][^456]*){2}"
44123
44423
41423
上記では、4種類の文字列を紹介します。echo -e "41123\n44123\n44423\n41423"
私達の範囲の4つの数字だけを印刷します。
$ echo -e "41123\n44123\n44423\n41423"
41123
44123
44423
41423
この正規表現はどのように機能しますか? 0 個以上の「not[456]」文字と、1 個以上の [456] または 0 個以上の「not[456]」文字で構成される正規表現パターンを設定して、後者を 2 回検索します。
それでは、スクリプトでいくつかのアセンブリを実行してみましょう。
for (( CON1=10000; CON1<=99999; CON1++ )) ;
do
if echo $CON1 | grep -q -E "[^456]*([456][^456]*){2}"; then
echo $CON1
fi
done
上記のhead
&トリックを使用すると、動作してtail
いることがわかります。
$ ./cmd.bash | head -5
10044
10045
10046
10054
10055
$ ./cmd.bash | tail -5
99955
99956
99964
99965
99966
しかし、この方法は非常に遅いことが判明しました。問題はですgrep
。費用がかかり、ループで繰り返しごとにgrepを1回実行するので、約80,000回になります!
これを改善するために、grep
コマンドをループから移動してリストを生成した後、次のように数字をエコーしたスクリプトの元のバージョンを使用して一度実行できます。
$ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}"
メモ:forループを完全に廃棄し、コマンドラインツールを使用できますseq
。これにより、同じ数値シーケンスが生成されますseq 10000 99999
。
ライナー?
これを行う素晴らしい方法は、上記のコマンドから一連の数字を取得し、各数字の間にpaste
aを挿入するコマンドにパイプ+
し、その出力をコマンドライン計算機として実行することですbc
。
$ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}" | paste -s -d"+"
10044+10045+10046+10054+10055+10056+10064+10065+10066+10144+10145+...
$ ./cmd.bash | grep -E "[^456]*([456][^456]*){2}" | paste -s -d"+" | bc
2409327540
しかし、これはこの問題を解決する全く異なる方法なので、ループに戻りますfor
。
純粋なバッシュを使う
したがって、Bashの数字が正確に2桁であるかどうかをテストする方法が必要ですが、grep
80,000回呼び出すのと同じくらい高価ではありません。最新バージョンのBashには、=~
ANDを実行できる演算子を使用する機能が含まれていますgrep
。
#!/bin/bash
for (( CON1=10000; CON1<=99999; CON1++ )) ;
if [[ $CON1 =~ [^456]*([456][^456]*){2} ]]; then
echo $CON1
fi
done
これを実行することはまさに私たちが望むようです。
$ ./cmd1.bash | head -5
10044
10045
10046
10054
10055
$ ./cmd1.bash | tail -5
99955
99956
99964
99965
99966
確認の結果、41511で動作することがわかりました。
$ ./cmd1.bash | grep 41511
41511
引用する
答え3
私は純粋なBashスクリプトでこれを行う必要があるようですが、John1024のアルゴリズムをawkに変換すると、たくさん加速する:
awk 'BEGIN{k=0;for(i=10000;i<100000;i++){j=i;if(gsub(/[456]/,"",j)==2)k+=i};print k}'
bashのバージョン時間の1/20未満で実行され、str.count()
Pythonの組み込みメソッドを使用するPythonのバージョンよりわずかに高速です。