あなたはBashを知らない:Bash配列の紹介
ソフトウェアエンジニアは開発の多くの側面でコマンドラインを定期的に使用していますが、アレイはコマンドラインのよりあいまいな機能の1つである可能性があります(ただし、そうではありません)正規表現演算子=~
のようにあいまいです )。しかし、あいまいで疑わしい構文は別として、Bash配列は非常に強力な場合があります。
待ってください、でもなぜですか?
Bashについて書くのは難しいです。なぜなら、記事が構文の奇妙さに焦点を当てたマニュアルに移るのは非常に簡単だからです。ただし、この記事の目的は、RTFMを使用しないようにすることです。
実際の(実際に役立つ)例
そのために、実際のシナリオと、Bashがどのように役立つかを考えてみましょう。社内のデータパイプラインの実行時間を評価および最適化するために、会社で新しい取り組みを主導しています。最初のステップとして、パラメータースイープを実行して、パイプラインがスレッドをどの程度活用しているかを評価します。簡単にするために、パイプラインをコンパイル済みのC ++ブラックボックスとして扱います。ここで調整できるパラメーターは、データ処理用に予約されているスレッドの数だけです。./pipeline --threads 4
。
最初に行うことは、--threads
の値を含む配列を定義することです。 テストするパラメータ:
allThreads=(1 2 4 8 16 32 64 128)
この例では、すべての要素が数値ですが、そうである必要はありません。Bashの配列には、数値と文字列の両方を含めることができます。例:myArray=(1 2 "three" 4 "five")
は有効な式です。また、他のBash変数と同様に、等号の前後にスペースを入れないようにしてください。それ以外の場合、Bashは変数名を実行するプログラムとして扱い、=
最初のパラメータとして!
配列を初期化したので、その要素のいくつかを取得しましょう。 echo $allThreads
を実行するだけであることに気付くでしょう。 最初の要素のみを出力します。
その理由を理解するために、一歩下がって、通常Bashで変数を出力する方法を再検討してみましょう。次のシナリオを検討してください。
type="article"
echo "Found 42 $type"
変数$type
と言います 単数名詞として与えられ、s
を追加したい 私たちの文の終わりに。 s
を単純に追加することはできません $type
へ これは別の変数に変わるため、$types
。また、echo "Found 42 "$type"s"
などのコードのゆがみを利用することもできますが 、この問題を解決する最善の方法は、中括弧を使用することです:echo "Found 42 ${type}s"
、これにより、変数の名前の開始位置と終了位置をBashに伝えることができます(興味深いことに、これは、テンプレートリテラルに変数と式を挿入するためにJavaScript / ES6で使用される構文と同じです)。
結局のところ、Bash変数は通常中括弧を必要としませんが、配列には必要です。次に、これにより、アクセスするインデックスを指定できます。たとえば、echo ${allThreads[1]}
配列の2番目の要素を返します。角かっこは含まれません。例:echo $allThreads[1]
、Bashが[1]
を処理するように導きます 文字列として出力します。
はい、Bash配列の構文は奇妙ですが、他のいくつかの言語とは異なり、少なくともインデックスはゼロです(R
。
上記の例では、配列に整数インデックスを使用しましたが、そうでない場合を2つ考えてみましょう。まず、$i
が必要な場合 -配列の-番目の要素。ここで、$i
は対象のインデックスを含む変数であり、次を使用してその要素を取得できます:echo ${allThreads[$i]}
。次に、配列のすべての要素を出力するために、数値インデックスを@
に置き換えます。 シンボル(@
と考えることができます all
の略として ):echo ${allThreads[@]}
。
それを念頭に置いて、$allThreads
をループしてみましょう --threads
の値ごとにパイプラインを起動します :
for t in ${allThreads[@]}; do
./pipeline --threads $t
done
配列インデックスのループ
次に、少し異なるアプローチを考えてみましょう。配列の要素をループするのではなく 、配列のインデックスをループできます :
for i in ${!allThreads[@]}; do
./pipeline --threads ${allThreads[$i]}
done
それを分解しましょう:上で見たように、${allThreads[@]}
配列内のすべての要素を表します。感嘆符を追加して${!allThreads[@]}
すべての配列インデックス(この場合は0から7)のリストを返します。つまり、for
ループはすべてのインデックスをループしています$i
$i
を読みます -$allThreads
の5番目の要素 --threads
の値を設定します パラメータ。
これは目にはかなり厳しいので、そもそもなぜわざわざ紹介するのか不思議に思うかもしれません。これは、ループ内のインデックスと値の両方を知る必要がある場合があるためです。たとえば、配列の最初の要素を無視したい場合、インデックスを使用すると、ループ内でインクリメントする追加の変数を作成する手間が省けます。 。
これまでのところ、--threads
ごとにパイプラインを起動することができました。 興味を持っている。ここで、パイプラインへの出力が秒単位の実行時間であると仮定しましょう。各反復でその出力をキャプチャし、それを別の配列に保存して、最後にさまざまな操作を実行できるようにします。
しかし、コードに飛び込む前に、さらにいくつかの構文を導入する必要があります。まず、Bashコマンドの出力を取得できる必要があります。これを行うには、次の構文を使用します:output=$( ./my_script.sh )
、コマンドの出力を変数$output
に格納します 。
必要な構文の2番目のビットは、取得した値を配列に追加する方法です。そのための構文はおなじみのように見えます:
myArray+=( "newElement1" "newElement2" )
すべてをまとめると、パラメータスイープを起動するためのスクリプトは次のとおりです。
allThreads=(1 2 4 8 16 32 64 128)
allRuntimes=()
for t in ${allThreads[@]}; do
runtime=$(./pipeline --threads $t)
allRuntimes+=( $runtime )
done
そしてvoilà!
他に何がありますか?
この記事では、パラメータースイープに配列を使用するシナリオについて説明しました。しかし、Bash配列を使用する理由は他にもあると思います。ここにさらに2つの例があります。
このシナリオでは、アプリはモジュールに分割され、それぞれに独自のログファイルがあります。特定のモジュールに問題の兆候がある場合に、適切な人に電子メールを送信するためのcronジョブスクリプトを作成できます。
# List of logs and who should be notified of issues
logPaths=("api.log" "auth.log" "jenkins.log" "data.log")
logEmails=("jay@email" "emma@email" "jon@email" "sophia@email")
# Look for signs of trouble in each log
for i in ${!logPaths[@]};
do
log=${logPaths[$i]}
stakeholder=${logEmails[$i]}
numErrors=$( tail -n 100 "$log" | grep "ERROR" | wc -l )
# Warn stakeholders if recently saw > 5 errors
if [[ "$numErrors" -gt 5 ]];
then
emailRecipient="$stakeholder"
emailSubject="WARNING: ${log} showing unusual levels of errors"
emailBody="${numErrors} errors found in log ${log}"
echo "$emailBody" | mailx -s "$emailSubject" "$emailRecipient"
fi
done
APIクエリ
中程度の投稿に最もコメントしているユーザーに関する分析を生成するとします。データベースに直接アクセスできないため、SQLは問題外ですが、APIを使用できます。
API認証とトークンについての長い議論に巻き込まれるのを避けるために、代わりに、公開APIテストサービスであるJSONPlaceholderをエンドポイントとして使用します。各投稿にクエリを実行し、コメントしたすべての人のメールを取得したら、それらのメールを結果配列に追加できます。
endpoint="https://jsonplaceholder.typicode.com/comments"
allEmails=()
# Query first 10 posts
for postId in {1..10};
do
# Make API call to fetch emails of this posts's commenters
response=$(curl "${endpoint}?postId=${postId}")
# Use jq to parse the JSON response into an array
allEmails+=( $( jq '.[].email' <<< "$response" ) )
done
ここでは、jq
を使用していることに注意してください コマンドラインからJSONを解析するためのツール。 jq
の構文 この記事の範囲を超えていますが、調査することを強くお勧めします。
ご想像のとおり、Bashアレイを使用すると役立つシナリオは他にも無数にあります。この記事で概説した例が、あなたに思考の糧を与えてくれることを願っています。自分の作品から共有できる他の例がある場合は、以下にコメントを残してください。
でも待ってください、もっとあります!
この記事ではかなりの配列構文を取り上げたので、ここで取り上げた内容の要約と、取り上げなかったいくつかのより高度なトリックを示します。
構文 | 結果 |
---|---|
arr=() | 空の配列を作成する |
arr=(1 2 3) | アレイを初期化する |
${arr[2]} | 3番目の要素を取得する |
${arr[@]} | すべての要素を取得する |
${!arr[@]} | 配列インデックスを取得する |
${#arr[@]} | 配列サイズを計算する |
arr[0]=3 | 最初の要素を上書きする |
arr+=(4) | 値を追加 |
str=$(ls) | ls を保存します 文字列として出力 |
arr=( $(ls) ) | ls を保存します ファイルの配列として出力 |
${arr[@]:s:n} | インデックスsから始まるstarting at index s |
私たちが発見したように、Bash配列は確かに奇妙な構文を持っていますが、この記事がそれらが非常に強力であることをあなたに納得させてくれることを願っています。構文のコツをつかむと、Bash配列を頻繁に使用していることに気付くでしょう。
BashまたはPython?
どちらが疑問を投げかけます: Pythonなどの他のスクリプト言語の代わりにBash配列を使用する必要があるのはいつですか?
私にとって、それはすべて依存関係に要約されます。コマンドラインツールの呼び出しのみを使用して目前の問題を解決できる場合は、Bashを使用することをお勧めします。ただし、スクリプトがより大きなPythonプロジェクトの一部である場合は、Pythonを使用することをお勧めします。
たとえば、パラメータスイープを実装するためにPythonを使用することもできますが、Bashのラッパーを作成するだけで済みます:
import subprocess
all_threads = [1, 2, 4, 8, 16, 32, 64, 128]
all_runtimes = []
# Launch pipeline on each number of threads
for t in all_threads:
cmd = './pipeline --threads {}'.format(t)
# Use the subprocess module to fetch the return output
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
output = p.communicate()[0]
all_runtimes.append(output)
この例ではコマンドラインを回避できないため、Bashを直接使用することをお勧めします。
この記事は、私がOSCONで行った、ライブコーディングワークショップ You Do n't Know Bashの講演に基づいています。 。スライドもクリッカーもありません。コマンドラインで入力している私と聴衆だけが、Bashの素晴らしい世界を探索しています。
-
おそらく知らない USB ドライブの 5 つの使い方
ある PC から別の PC にデータを転送するために最初に思い浮かぶデバイスは、USB またはペン ドライブです。間違いなく、今日のストレージ メディアは新たな高みに達していますが、多くの人はデータの転送だけでなく保存にも USB を使用しています。 しかし、USB の使用はストレージ メディアとして限定されないことをご存知でしたか。 USB はより強力で、コンピューターのロックやロック解除など、他の多くの便利な目的に使用できます。 PC のパフォーマンスの向上など。 信じられない?次に、この記事を読んで、知らないかもしれない USB スティックの 5 つの使い方を学んでください。 コンピ
-
Wi-Fi 6:知っておくべきことすべて!
今年は技術的なサプライズがたくさん待っています! Wi-Fi 6 もその 1 つです。はい、そうです。 2019 年に目にする 1 つのゲーム チェンジャー ワイヤレス標準である次世代の Wi-Fi に備えましょう。テクノロジーやガジェットの傾向がますます強くなっているため、Wi-Fi 6 の登場は確かに 1 つの良いニュースになります。特に混雑したエリアでは、速度とパフォーマンスが向上します。そのため、Wi-Fi 6 は 802.11ax としても知られる最新の Wi-Fi 標準であり、今年リリースされる予定です。 情報筋によると、Wi-Fi 6 がリリースされると、これらの数値がデバイス