GNU/Linux >> Linux の 問題 >  >> Linux

自動化のためのBashの使用

Bashの基本的な側面をカバーする最近の一連の記事(この記事の最後にリストされています)を考えると、新しい同僚の1人が1つをクラウドに投げ込むことは避けられません。これらが進むにつれて、次の論理的なステップは次のとおりです。

  1. 絶対に重要なことを確認するには、「クラウド」スクリプトが問題なく機能している必要があります。
  2. スクリプトの元の作成者を確認し、実際にどのように機能するかを完全に忘れています。
  3. 最新の管理者が、検証なしで根本的に変更するように任務を負っていることを確認します。

この記事では、すべての管理者を支援し、上記のすべての間違いを回避できるように支援します。その結果、より幸せな経営につながり、うまくいけば私たちの継続的な雇用につながります。

「Bashスクリプト」の綴り方

悟りを開くために(そして$ DEITYを愛するために)、コードをソースコード管理(SCM)ツールにチェックインしてください。学習しているときでも、ローカルリポジトリを遊び場として使用してください。この方法により、時間の経過とともに努力を記録できるだけでなく、間違いを簡単に元に戻すことができます。 gitの使用を開始するためのすばらしい記事がたくさんあります 強くお勧めします。

Bashの使用と学習についての簡単なメモ。このスクリプト言語は、独自の一連の-ismsと文体の作者の好みをもたらすためです。新しいもの(構文、スタイル、または言語構成)を見つけたら、すぐに調べてください。 。マニュアルページ(最初の選択肢)またはAdvanced Bash Scripting Guide(どちらもオフラインでアクセスできます)から新しい項目を理解するために時間を費やしてください。これを行うと、最初は遅くなりますが、時間が経つにつれて、この練習は、答えを見つける場所についての知識を構築するのに役立ちます。

再利用可能なBashナゲットをライブラリとして書き込む

自動化のスクリプトは、Unix哲学を取り入れながら作成するのが最適です。1つのことだけを実行する多くの小さなツールです。つまり、1つの巨大な「キッチンシンク」を使用するよりも、小さくて特殊なスクリプトやライブラリを作成する方がはるかに優れています。確かに、私はいくつかの巨大なものを書き、維持しました(時々それらは目的を果たします)。

自動化スクリプトは、多くの場合、複数の作成者が理解でき、保守できる必要があります。多くの小さなスクリプトが飛び交う(そしてバージョン管理で追跡される)と、名前、バージョン、パス、またはURLの値への参照を共有する必要があることにすぐに気付くでしょう。これらの共通の要素をライブラリに書き込むことで、メンテナがインラインドキュメントを評価するための追加の精神的なスペースも提供されます。さらに、そうすることで、単体テストの活用がほぼ簡単になります(このトピックについては最後に説明します)。

ローカルの「プレイ」リポジトリを作成して、最初から優れたコード衛生を実践しましょう。新しいリポジトリ内に、スクリプトとライブラリファイルを格納する場所を作成します。簡単にするために、よく理解されているFHS標準に固執するのが好きです。ディレクトリ./bin/を作成します および./lib/ リポジトリのルートにあります。大規模な自動化プロジェクトでは、これらの名前を引き続き使用しますが、深く埋もれている可能性があります(たとえば、scripts またはtools サブディレクトリ)。

パスについて話すと、最初のライブラリのすばらしいトピックにたどり着きます。将来のコンポーネントが構造要素と高レベルの値を参照する方法が必要です。お気に入りのエディタを使用して、ファイル./lib/anchors.shを作成します 以下のコンテンツを追加してください:



# A Library of fundamental values
# Intended for use by other scripts, not to be executed directly.

# Set non-'false' by nearly every CI system in existence.
CI="${CI:-false}"  # true: _unlikely_ human-presence at the controls.
[[ $CI == "false" ]] || CI='true'  # Err on the side of automation

# Absolute realpath anchors for important directory tree roots.
LIB_PATH=$(realpath $(dirname "${BASH_SOURCE[0]}"))
REPO_PATH=$(realpath "$LIB_PATH/../")  # Specific to THIS repository
SCRIPT_PATH=$(realpath "$(dirname $0)")

ファイルは2行の空白行で始まり、最初のコメントでその理由が説明されています。ライブラリはすべきではありません 実行可能ファイルとして設定されます(名前が.shで終わり、そのタイプを示しているにもかかわらず)。ライブラリが直接実行された場合、ユーザーのシェルが消える(または悪化する)可能性があります。直接実行の無効化(chmod -x ./lib/anchors.sh )は、初心者の管理者保護の最初のレベルです。ファイルの先頭にあるコメントは第2レベルです。

慣例により、コメントは常に理由を説明する必要があります (ではありません )次のステートメントの。読者はステートメントを読んでそれが何をしているのかを理解することはできますが、著者がその時点で何を考えていたかを確実に理解することはできません。ただし、深く掘り下げる前に、Bashで人々を不意を突かれることが多い問題について詳しく説明する必要があります。

Bashのデフォルトでは、未定義の変数を参照すると空の文字列が提供されます。 CI 変数(または自動化で類似したもの)は、人間がいない可能性を示すことを目的としています。残念ながら、ロボットの大君主にとって、人間はおそらく少なくとも1回は手動でスクリプトを実行する必要があります。この時点で、CIの値を設定するのを忘れる可能性があります。 Enterキーを押す前に。

したがって、値のデフォルトを設定し、それが常にtrueまたはfalseのいずれかであることを確認する必要があります。上記のライブラリコードの例は、文字列が空であるかどうかをテストする方法と、文字列に値のペアの1つを含めるように強制する方法の両方を示しています。 anchors.shの最初のステートメントグループの読み方 は:

'CIを定義します 'の結果として今後:

  1. CIの以前の値を調べる (未定義の可能性があるため、空の文字列です)。
  2. ':- '部分は意味します:
    1. 値が空の文字列の場合は、文字列'falseを表します '代わりに。
    2. 値が空の文字列でない場合は、それが何であれ('DaRtHを含む)を使用します。 VaDeR ')。

'[[内のものをテストします 'および']] ':

  1. 'CIの新しい値の場合 'はリテラル文字列"falseに等しい "、終了コード0をスローします (成功または真実を意味します。)
  2. それ以外の場合は、終了コード1をスローします (失敗または非真実を意味する)

テストが0で終了した場合 、次の行に進む、または('|| 'パート)、初心者の管理者セットCI=YES!PLEASEを想定します または完全なマシンセットCI=true 。 'CIの値をすぐに設定します 'リテラル文字列'true ';完璧なマシンの方が優れているので、ミスタクしないでください。

このライブラリの残りの部分では、アンカーパス値は、ほとんどの場合、リポジトリから自動化で実行されるスクリプトで役立ちます。大規模なプロジェクトで使用する場合は、リポジトリルートを基準にしてライブラリディレクトリの相対的な場所を調整する必要があります。それ以外の場合は、読者のための調査演習として、これらのステートメントを理解したままにしておきます(今すぐ実行してください。後で役立ちます)。

BashスクリプトでのBashライブラリの使用

ライブラリをロードするには、sourceを使用します 組み込みコマンド。このコマンドは派手ではありません。ファイルの場所を指定すると、その場でコンテンツが読み取られて実行されます。つまり、ライブラリコードの実行時のコンテキスト 実際には、sourceするスクリプトになります

脳の多くが耳から滴り落ちるのを防ぐために、ここに簡単な./bin/example.shがあります。 説明するスクリプト:

#!/bin/bash

LIB_PATH="$PWD/$(dirname $0)/../lib/anchors.sh"
echo "Before loading: $LIB_PATH"
set -ax
cd /var/tmp
source $LIB_PATH
echo "After loading: $(export -p | grep ' LIB_PATH=')"

ライブラリをロードする前に、スクリプトが実行時コンテキストを変更したことにすぐに気付くかもしれません。また、LIB_PATHも定義します ローカルで、相対パス(説明のため)を使用してファイル(紛らわしいことに、ディレクトリではなく)をポイントします。

先に進み、このスクリプトを実行して、出力を調べます。ライブラリanchors.shのすべての操作に注意してください。 /var/tmp/内で実行されました ディレクトリとその定義を自動的にエクスポートしました。 LIB_PATHの古い定義 aによって破壊され、エクスポートされました set -axで 。この事実は、declare -xからの出力に表示されます。 exportからの送信 指図。うまくいけば、デバッグ出力(x set -axで )理解できます。

このようにデバッグする場合、Bashはすべての行を解析するときにすべての中間値を出力します。 set -axを絶対に使用したくない理由を示すために、このスクリプトをここに含めました。 または、ライブラリの最上位からのコマンドを使用してディレクトリを変更します。ライブラリ命令は、スクリプトのロード時に評価されることに注意してください。したがって、ライブラリ内の環境を変更すると、sourceを使用するスクリプトに実行時の副作用が発生します。 それをロードします。このような副作用は、少なくとも1人のシステム管理者を完全に狂わせることが保証されています。あなたは決して知らない、その管理者は私かもしれないので、それをしないでください。

実際の例として、ユーザー名/パスワード環境変数を使用してリモートサービスにアクセスする関数を定義する架空のライブラリについて考えてみます。ライブラリがトップレベルのset -axを実行した場合 この関数の前に、それがロードされるたびに、デバッグ出力にはこれらの変数の表示が含まれ、誰もが見ることができるようにあなたの秘密がいたるところに飛び散ります。さらに悪いことに、初心者の同僚がキーボードに怒鳴らずに出力を無効にすることは(呼び出しスクリプトの観点から)困難になります。

結論として、ここで重要なのは、ライブラリが発信者のコンテキスト内で「発生」することを認識し続けることです。この要因は、例のanchors.shの理由でもあります。 を使用できます $0 (実行可能スクリプトのパスとファイル名)が、ライブラリ自体へのパスは、「マジック」'${BASH_SOURCE[0]}'を介してのみ利用できます。 (配列要素)。この要因は最初は混乱するかもしれませんが、規律を保つように努める必要があります。ライブラリ内の広範囲にわたる広範囲にわたるコマンドは避けてください。あなたがそうするとき、すべての新しい管理者の採用はあなたのドーナツの支払いを主張するでしょう。

ライブラリの単体テストの記述

ユニットテストを作成することは、完全なカバレッジが通常は時間の無駄であることに気付くまで、困難な作業のように感じることがあります。ただし、ライブラリコードに触れるときは、常にテストコードを使用して更新することをお勧めします。テスト作成の目標は、最も一般的で明白なユースケースを処理してから次に進むことです。コーナーケースや、すべての時間の使用よりも少ないことにあまり注意を払わないでください。また、最初は、統合テストではなく、単体テストレベルでライブラリテストの取り組みに集中することをお勧めします。

別の例を見てみましょう:実行可能スクリプト./lib/test-anchors.sh

#!/bin/bash

# Unit-tests for library script in the current directory
# Also verifies test script is derived from library filename

TEST_FILENAME=$(basename $0)  # prefix-replace needs this in a variable
SUBJ_FILENAME="${TEST_FILENAME#test-}"; unset TEST_FILENAME
TEST_DIR=$(dirname $0)/

ANY_FAILED=0

# Print text after executing command, set ANY_FAILED non-zero on failure
# usage: test_cmd "description" <command> [arg...]

test_cmd() {
   local text="${1:-no test text given}"
   shift
   if ! "$@"; then
      echo "fail - $text"; ANY_FAILED=1;
   else
      echo "pass - $text"
   fi
}

test_paths() {
   source $TEST_DIR/$SUBJ_FILENAME
   test_cmd "Library $SUBJ_FILENAME is not executable" \
      test ! -x "$SCRIPT_PATH/$SUBJ_FILENAME"
   test_cmd "Unit-test and library in same directory" \
      test "$LIB_PATH" == "$SCRIPT_PATH"
   for path_var in LIB_PATH REPO_PATH SCRIPT_PATH; do
      test_cmd "\$$path_var is defined and non-empty: ${!path_var}" \
         test -n "${!path_var}"
      test_cmd "\$$path_var referrs to existing directory" \
         test -d "${!path_var}"
   done
}

# CI must only/always be either 'true' or 'false'.
# Usage: test_ci <initial value> <expected value>

test_ci() {
   local prev_CI="$CI"  # original value restored at the end
   CI="$1"
   source $TEST_DIR/$SUBJ_FILENAME
   test_cmd "Library $SUBJ_FILENAME loaded from $TEST_DIR" \
      test "$?" -eq 0
   test_cmd "\$CI='$1' becomes 'true' or 'false'" \
      test "$CI" = "true" -o "$CI" = "false"
   test_cmd "\$CI value '$2' was expected" \
      test "$CI" = "$2"
   CI="$prev_CI"
}

test_paths
test_ci "" "false"
test_ci "$RANDOM" "true"
test_ci "FoObAr" "true"
test_ci "false" "false"
test_ci "true" "true"

# Always run all tests and report, exit non-zero if any failed

test_cmd "All tests passed" \
   test "$ANY_FAILED" -eq 0
[[ "$CI" == "false" ]] || exit $ANY_FAILED  # useful to automation
exit(0)

このスクリプトを./libに配置した理由 (./binとは対照的 )は便利ではなく、テストがチェックしているコードの使用に依存してはならないためです。このテストではパスをチェックする必要があるため、ライブラリと同じパスに配置する方が簡単です。そうでなければ、このアプローチは個人的な好みの問題です。コードを理解するのに役立つ可能性があるため、今すぐテストを実行してください。

まとめ

この記事は、自動化でBashを使用する全体を表すものではありません。しかし、私は(従えば)間違いなくあなたの生活を楽にする基本的な知識と推奨事項を植え付けようとしました。そうすれば、物事が困難になった場合でも、ランタイムコンテキストの重要性をしっかりと理解することが役立ちます。

最後に、自動化のための、または自動化のためのスクリプトは、間違いを許さない可能性があります。ライブラリの基本的な単体テストを実施することで、自信を深め、次の人が来るのを助けることができます(5年間忘れた後のあなたかもしれません)。この記事で使用されているすべてのサンプルコードは、ここからオンラインで見つけることができます。

Bashの基本をブラッシュアップすることに興味がありますか?チェックアウト:

  • Bashを使用してプログラミングする方法:論理演算子とシェル拡張
  • Bashスクリプトを改善する5つの方法
  • シェルスクリプトの使用を開始する
  • 知っておく必要のある10の基本的なLinuxコマンド
  • Linux環境変数のヒントとコツ

[Red Hat Enterprise Linuxを試してみませんか?今すぐ無料でダウンロードしてください。 ]


Linux
  1. Bashスクリプトでのエラー処理

  2. ネットワークのトラブルシューティングにSSツールを使用する

  3. Bash スクリプト:セッションをログに記録するための bash スクリプトからのスクリプト コマンドの使用

  1. Bashスクリプトに.shまたは.bash拡張子を使用しますか?

  2. BashスクリプトでのLinuxスリープコマンドの使用

  3. BashスクリプトでのLinuxBasenameコマンドの使用

  1. Bash For Loop

  2. Bashスクリプトでコメントを書く

  3. bash スクリプトを使用した telnet セッションの自動化