完全なbashスクリプトを関数として書くのはなぜですか?

完全なbashスクリプトを関数として書くのはなぜですか?

職場では、bashスクリプトをたくさん書いています。私の上司は、次の例と同様に、スクリプト全体を関数に分割することを提案しました。

#!/bin/bash

# Configure variables
declare_variables() {
    noun=geese
    count=three
}

# Announce something
i_am_foo() {
    echo "I am foo"
    sleep 0.5
    echo "hear me roar!"
}

# Tell a joke
walk_into_bar() {
    echo "So these ${count} ${noun} walk into a bar..."
}

# Emulate a pendulum clock for a bit
do_baz() {
    for i in {1..6}; do
        expr $i % 2 >/dev/null && echo "tick" || echo "tock"
        sleep 1
    done
}

# Establish run order
main() {
    declare_variables
    i_am_foo
    walk_into_bar
    do_baz
}

main

より多くのコメントといくつかの行間隔で確立できる「読みやすさ」に加えて、これを行う理由はありますか?

スクリプトをより効率的に実行させますか(実際にはその逆であると予想します)、または上記の読みやすさを超えてコードを変更するのは簡単ですか?それとも、これは単なるスタイルの好みにすぎませんか?

スクリプトはこれをうまく示していませんが、実際のスクリプトの関数の「実行順序」は、実行されたタスクと設定されたタスクに応じてwalk_into_bar非常に線形的な傾向があるため、実行を交換できます。ランダムに、シーケンスは私たちが一般的に行うことではありません。たとえば、突然物を後ろに入れたくないでしょう。そうすれば、物事が壊れるでしょう。i_am_foodo_bazwalk_into_bardeclare_variableswalk_into_bar

上記のスクリプトを作成する方法の例は次のとおりです。

#!/bin/bash

# Configure variables
noun=geese
count=three

# Announce something
echo "I am foo"
sleep 0.5
echo "hear me roar!"

# Tell a joke
echo "So these ${count} ${noun} walk into a bar..."

# Emulate a pendulum clock for a bit
for i in {1..6}; do
    expr $i % 2 >/dev/null && echo "tick" || echo "tock"
    sleep 1
done

答え1

読みやすさそれは同じです。しかし、もっとたくさんあります。モジュラーそれだけではない。 (半モジュール式おそらく機能により正確です。 )

関数内の一部の変数をローカル変数として保持することで増加できます。信頼できる、物事が汚れる可能性を減らします。

関数のもう一つの利点は再利用性。関数がコーディングされると、スクリプトで複数回適用できます。他のスクリプトに移植することもできます。

あなたのコードは現在直線的である可能性がありますが、将来は線形領域に移動する可能性があります。マルチスレッドまたは多重処理バッシュの世界で。関数で作業する方法を学んだら、並列処理に進む準備が整いました。

もう1つ追加することがあります。 Etsitpab Niolivが以下の説明に示すように、機能から一貫したエンティティにリダイレクトするのは簡単です。ただし、機能リダイレクトには別の側面があります。つまり、関数定義に従ってリダイレクトを設定できます。例えば。 :

f () { echo something; } > log

これで、関数呼び出しに明示的なリダイレクトは必要ありません。

$ f

これにより、重複を避けることができ、安定性が向上し、整理された内容を維持するのに役立ちます。

また、見ることができます

答え2

この記事を読んだ後、私は同じスタイルのbashプログラミングを使い始めました。Kfir Laviのブログ記事「防御的バッシュプログラミング」。彼は妥当な理由をたくさん提示していますが、個人的には次のことが最も重要だと思います。

  • プロセスが説明的になります。コードの特定の部分が何をすべきかを理解するのは簡単です。コードウォールの代わりに「ああ、このfind_log_errors関数はそのログファイルからエラーを読みました」というメッセージが表示されます。神は長いスクリプトの真ん中にどのような正規表現があるかを知っているので、 awk/grep/sed 行を探すのと比較してみてください。コメントがなければ、そこで何をしているのかわかりません。

  • とを束ねてset -x関数をデバッグできますset +x。残りのコードが正しく機能していることがわかったら、このトリックを使用して特定の機能のみをデバッグすることに集中できます。もちろん、スクリプトの一部を添付できますが、長すぎるとどうなりますか?これを行う方が簡単です。

       set -x
       parse_process_list
       set +x
    
  • 印刷の使い方はと同じですcat <<- EOF . . . EOF。コードをより専門的にするために何度も使用しました。しかもparse_args()機能getoptsもかなり便利です。繰り返しますが、これは巨大なテキストの壁のようにスクリプトにすべてを押し込むよりも読みやすさに役立ちます。これらは再利用も簡単です。

明らかに、これはC、Java、またはValaを知っていますが、bashの経験が限られている人はより読みやすくなります。効率性の点でできることはあまりありません。 bash自体は最も効率的な言語ではなく、人々はスピードと効率のためにPerlとPythonを好みます。ただし、次のnice機能を使用できます。

nice -10 resource_hungry_function

これは、コード内のすべての行でniceを呼び出すよりも多くの入力操作を減らし、優先度の低いスクリプトの一部のみを実行したい場合に役立ちます。

私の考えでは、バックグラウンドで複数のステートメントを実行したい場合は、バックグラウンドで関数を実行することも役立ちます。

このスタイルを使用するいくつかの例は次のとおりです。

答え3

私の意見では、私は関数の3つの利点を述べました。

  1. 精度をテストして確認する方が簡単です。

  2. 関数は将来のスクリプトで簡単に再利用できます(ソース)

  3. あなたの上司がそれらを愛しています。

そして、3番目の項目の重要性を決して過小評価しないでください。

また、問題を解決したいと思います。

...したがって、実行順序をランダムに変更することは、通常実行することではありません。たとえば、突然物をdeclare_variables後ろに入れたくないでしょうwalk_into_bar。そうすれば、物事が壊れるでしょう。

コードを関数に分割することの利点を得るには、関数をできるだけ独立して作成する必要があります。他の場所で使用されていない変数が必要な場合は、そのwalk_into_bar変数をで定義してローカルに作成する必要がありますwalk_into_bar。コードを関数に分割し、相互依存性を最小限に抑えるプロセスは、コードをより明確でシンプルにする必要があります。

理想的には、関数を個別にテストするのは簡単です。相互作用のために簡単にテストできない場合、これはリファクタリングによって利点が得られることを示しています。

答え4

コードを関数に分解する理由は、C / C ++、Python、Perl、Ruby、または他のプログラミング言語のコードを分解する理由と同じです。より深い理由は抽象化です。下位レベルのタスクを上位レベルの基本要素(関数)にカプセル化するため、タスクの実行方法について心配する必要はありません。同時に、コードはより読みやすく保守が容易になり、プログラムロジックがより明確になります。

しかし、あなたのコードを見ると、変数を宣言する関数があることが奇妙であることがわかりました。

関連情報