あなたはこのbashスクリプトパズルを解くことができますか?

はい、私はITを知っています。それはFOSSです。 今週のチャレンジでは、端末画面を表示します。そして、私たちが望んだ結果を得るのを手助けするためにあなたを頼りにします。 多くの解決策があり得ます、そして創造的であることは挑戦の最も面白い部分です。

まだ行っていない場合は、以前の課題を見てください。

  • バッシュチャレンジ6
  • バッシュチャレンジ5

あなたは本の形でこれらの課題を(未発表の課題で)購入し、私たちをサポートすることもできます。

プレイする準備はできましたか? だから今週の挑戦はここにあります。

トークンカウンター

今週は、より「プログラミング指向」の課題に戻ります。 説明は少し抽象的なので、数分間私と一緒にいるようにしてください - そして私は以下の説明が十分に明確になることを願っています:

私は「RED」または「BLUE」のトークンのストリームを持っています。 必要に応じて、例えばイベントストリームの表現としてそれを考えることができます。 私はそのストリームを特に制御することはできません。 予想外に、どちらか一方のトークンが生成されることを私は知っています。 そして私は、蒸気は有限であることを知っています(すなわち、ある時点で、それ以上読むべきデータがなくなるでしょう)。

この課題のために、私はそのストリームを生成するためにBash関数を使用しました。 あなたはとにかくそれを変更することはできません。

# You MUST NOT change that : stream() { TOKENS=( "RED" "BLUE" ) for((i=0;i<100;++i)) ; do echo ${TOKENS[RANDOM%2]} done } 

私の目標は彼の流れの中にあったRED BLUEトークンの両方の数を数えることです。 私自身では、REDトークンの数だけを数えるための解決策を見つけることができました。

  # You MUST change that stream | \ grep -F RED | wc -l > RED.CNT cat RED.CNT 

残念ながら、REDトークン BLUEトークンの両方を数えるための解決策は見つかりませんでした。 だからこそ私はあなたの助けが必要です。 何か案が ?

以下のコメント欄であなたの解決策を読むのを楽しみにしています!

詳細が少ない

この課題を解決するために、私は使用しました:

  • GNU Bash、バージョン4.4.5(x86_64-pc-linux-gnu)

  • Debian 4.8.7-1(amd64)
  • すべてのコマンドは、標準のDebianディストリビューションに同梱されているものです。
  • エイリアスが設定されたコマンドはありません

ソリューション

再現方法

これが、この課題を解決するために使用した生のコードです。 これを端末で実行すると、チャレンジの図に表示されているのとまったく同じ結果を再現できます(私と同じソフトウェアバージョンを使用していると仮定します)。

 rm -rf ItsFOSS mkdir -p ItsFOSS cd ItsFOSS clear stream() { TOKENS=( "RED" "BLUE" ) for((i=0;i RED.CNT cat RED.CNT 

なにが問題だったの ?

ここでの唯一の問題は、データストリームを直接 grep送信するため、最初の試行で入力の一部を破棄することでした。

基本的にその問題を解決するための3つのアプローチがあります:

  • ストリームデータを保存し、後でそれらを処理します。

  • ストリームを複製し、REDトークンとBLUEトークン用に2つの独立したパスを処理します。
  • 両方のケースを同じコマンドで処理してください。

その価値のために、それぞれの解決策の後に、私は私のシステムで観察されたリアルタイムの使用法を示します。 これはあくまで目安であり、慎重に行う必要があります。 だから、自分で比較してみてください。

店舗とプロセスのアプローチ

ストアアンドプロセスアプローチの最も簡単な実装は明らかです。

 stream > stream.cache grep -F RED RED.CNT grep -F BLUE BLUE.CNT rm stream.cache (1.3s for 10, 000, 000 tokens) 

機能しますが、いくつかの欠点があります。データを保存する必要があり、データはトークンごとに順番に処理されるということです。 もっと微妙なことですが、 stream.cacheファイルを2回読むと、並行プロセスが処理中にそのファイルを更新すると、競合状態になる可能性があります。

それでもストアアンドプロセスのカテゴリには、まったく異なる解決策があります。

 stream | sort | uniq -c (5.9s for 10, 000, 000 tokens) 

sortコマンドはそれらを処理することができる前にすべてのデータを (RAMまたはディスクのどちらかに)最初に読み込みそして保存しなければならないので、私はストアアンドプロセスアプローチを考慮します。 より正確には、私のDebianシステムでは、 sortコマンドは/tmprw権限を持ついくつかの一時ファイルを作成します。 基本的に、この解決方法は最初のものと同じ欠点を持っていますが、最も悪い性能を持っています。

複製ストリーム

それらを処理する前に/データを/保存する必要がありますか? いいえ。もっと賢い考えは、ストリームを2つの部分に分割し、各サブストリームで1種類のトークンを処理することです。

 stream | tee >(grep -F RED | wc -l > RED.CNT) \ >(grep -F BLUE | wc -l > BLUE.CNT) \ > /dev/null (0.8s for 10, 000, 000) 

ここには中間ファイルはありません。 teeコマンドは、ストリームデータを到着時に複製します。 各処理ユニットはそれ自身のデータのコピーを得て、それらをその場で処理することができる。

これは、データが到着したときにデータを処理するだけでなく、 並列処理を行うようになったため、賢い考えです。

到着時にデータを処理する

コンピュータサイエンスでは、おそらく以前の解決策が問題に対して機能的なアプローチを取ったと言うでしょう。 一方、次の解決策は純粋に必須の解決策になります。 ここでは、それぞれのトークンを読み、/これがREDトークンの場合、/ then / REDカウンターをインクリメントします。それ以外の場合/これがBLUEトークンの場合、BLUEカウンターをインクリメントします。

これは、そのアイデアの単純なBash実装です。

 declare -i RED=0 BLUE=0 stream | while read TOKEN; do case "$TOKEN" in RED) RED+=1 ;; BLUE) BLUE+=1 ;; esac done (103.2s for 10, 000, 000 tokens) 

最後に、 AWKコマンドの大ファンである私は、それをきちんとしたエレガントな方法で解決するためにそれを使用するという誘惑には抵抗しません。

 stream | awk ' /RED/ { RED++ } /BLUE/ { BLUE++ } END { printf "%5d %5d\n", RED, BLUE } ' (2.6s for 10, 000, 000 tokens) 

私のAWKプログラムは3つの規則で構成されています。

  • REDという単語を含む行に遭遇したとき、REDカウンターを増やします( ++

  • 単語BLUEを含む行に遭遇したとき、BLUEカウンターを増やします
  • 入力の終わりに、両方のカウンターを表示します。

もちろん、数学演算子のために、 初期化されていない AWK変数はゼロであると仮定されることをあなたが知っていなければならないことを完全に理解するために。

それは素晴らしい仕事です。 ただし、トークンごとに同じ規則を複製する必要があります。 ここには大したことはありません。2つの異なるトークンしかありません。 私たちがそれらをたくさん持っているともっと厄介です。 それを解決するために、 配列に頼ることができます

 stream | awk ' { C[$0]++ } END { printf "%5d %5d\n", C["RED"], C["BLUE"] } ' (2.0s for 10, 000, 000 tokens) 

ここで必要なのは、トークンの数に関係なく、2つの規則だけです。

  • 読み取りトークン( $0 )が何であれ、対応する配列セルを大きくします(ここでは、 C["RED"]またはC["BLUE"]いずれか)。

  • 入力の終わりに、 "RED" "BLUE"セルと"BLUE"セルの両方の配列の内容を表示します。

"RED""BLUE"が文字列になっていることに注意してください(それらの周りに二重引用符がありますか?)そしてAWKは連想配列をサポートしているのでこれは問題になりません。 また、プレーン変数と同じように、 AWK連想配列の初期化されていないセルは、数学演算子ではゼロと見なされます。

前に説明したように、ここではAWKを使用することを選択しました。 しかし、 Perlファンはこの問題について異なる意見を持っているかもしれません。 あなたがそれらのうちの1人なら、なぜあなた自身の解決策をコメント欄に投稿しませんか?

とにかく、私たちはあなたがその挑戦を楽しんだことを望みます。 そしてもっと楽しみにしていてください!

推奨されます

Ubuntuやその他のLinuxディストリビューションでYouTubeのビデオをダウンロードする3つの簡単な方法
2019
Linuxで利用可能な6つのベストメッセージングアプリ
2019
Linuxパワーユーザーバンドルコース($ 19)
2019