GNU/Linux >> Belajar Linux >  >> Linux

Menggunakan Bash untuk otomatisasi

Mengingat banyaknya artikel baru-baru ini yang membahas aspek dasar Bash (tercantum di akhir artikel ini), tidak dapat dihindari bahwa salah satu kolega baru Anda memasukkannya ke cloud. Seiring berjalannya waktu, langkah logis berikutnya adalah:

  1. Memverifikasi sesuatu yang sangat penting bergantung pada skrip "cloud" yang berfungsi dengan sempurna.
  2. Verifikasi penulis asli skrip, benar-benar lupa cara kerjanya.
  3. Memverifikasi bahwa admin terbaru ditugaskan untuk mengubahnya secara mendasar tanpa validasi apa pun.

Pada artikel ini, saya membantu semua admin dan membantu Anda menghindari semua kesalahan di atas. Pada gilirannya, hasilnya mengarah pada manajemen yang lebih bahagia, dan semoga pekerjaan kita berlanjut.

Cara mengeja "script Bash"

Untuk menjadi tercerahkan (dan untuk kecintaan Anda pada $DEITY) periksa kode Anda ke alat manajemen kode sumber (SCM). Bahkan saat Anda sedang belajar, gunakan repositori lokal sebagai taman bermain. Latihan ini tidak hanya memungkinkan Anda mencatat upaya Anda dari waktu ke waktu, tetapi juga memungkinkan Anda dengan mudah membatalkan kesalahan. Ada banyak artikel bagus tentang memulai git yang sangat saya rekomendasikan.

Catatan singkat tentang menggunakan dan mempelajari Bash, karena bahasa skrip ini menghadirkan serangkaian -isme dan preferensi penulis gaya yang unik:Segera setelah Anda menemukan sesuatu yang terlihat baru bagi Anda (sintaks, gaya, atau konstruksi bahasa) segera cari . Luangkan waktu Anda untuk memahami item baru dari halaman manual (pilihan pertama) atau Panduan Skrip Bash Tingkat Lanjut (keduanya dapat diakses secara offline). Melakukan hal ini pada awalnya akan memperlambat Anda, tetapi seiring waktu, praktik ini akan membantu Anda membangun pengetahuan tentang di mana menemukan jawaban.

Menulis nugget Bash yang dapat digunakan kembali sebagai perpustakaan

Skrip dalam otomatisasi paling baik ditulis sambil menganut filosofi Unix:Banyak alat kecil yang hanya melakukan satu hal. Artinya, Anda akan jauh lebih baik menulis skrip dan perpustakaan kecil dan khusus, daripada yang Anda lakukan dengan satu "wastafel dapur" raksasa. Meskipun, memang, saya telah menulis dan memelihara beberapa raksasa (kadang-kadang mereka memang memiliki tujuan).

Skrip otomatisasi sering kali harus dapat dimengerti dan dipelihara oleh lebih dari satu penulis. Dengan banyak skrip kecil terbang di sekitar (dan dilacak dalam kontrol versi), Anda akan segera menemukan diri Anda perlu berbagi referensi ke nilai untuk nama, versi, jalur, atau URL. Menulis elemen umum ini ke dalam perpustakaan juga menyediakan ruang mental tambahan bagi pengelola untuk menghargai dokumentasi sebaris. Selain itu, hal ini membuat pengujian unit pendongkrak menjadi hampir sepele (kita akan membahas topik ini di bagian akhir).

Mari kita praktikkan kebersihan kode yang baik dari awal dengan membuat repositori "play" lokal. Dalam repositori baru Anda, buat tempat untuk memuat skrip dan file perpustakaan kami. Saya ingin tetap menggunakan standar FHS yang dipahami dengan baik untuk kesederhanaan. Buat direktori ./bin/ dan ./lib/ di root repositori. Dalam proyek otomasi yang lebih besar, saya masih menggunakan nama-nama ini, tetapi mungkin terkubur dalam-dalam (misalnya, di bawah scripts atau tools subdirektori).

Berbicara tentang jalur membawa saya ke topik yang bagus untuk perpustakaan pertama. Kami membutuhkan cara untuk komponen masa depan kami untuk referensi elemen struktural dan nilai-nilai tingkat tinggi. Menggunakan editor favorit Anda, buat file ./lib/anchors.sh dan tambahkan konten di bawah ini:



# 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)")

File dimulai dengan dua baris kosong, dan komentar pertama menjelaskan alasannya. Perpustakaan seharusnya tidak ditetapkan sebagai executable (meskipun nama berakhiran .sh, menunjukkan jenisnya). Jika library dijalankan secara langsung, mungkin saja hal itu dapat menyebabkan shell pengguna menghilang (atau lebih buruk). Menonaktifkan eksekusi langsung (chmod -x ./lib/anchors.sh ) adalah tingkat pertama perlindungan admin pemula. Komentar di awal file adalah level kedua.

Sesuai aturan, komentar harus selalu menjelaskan mengapa (bukan apa ) dari pernyataan berikut. Seorang pembaca dapat dengan mudah membaca pernyataan tersebut untuk memahami apa yang dilakukannya, tetapi mereka tidak dapat dengan andal merasakan apa yang sedang dipikirkan penulis pada saat itu. Namun, sebelum saya masuk lebih dalam, saya perlu merinci masalah yang sering membuat orang lengah dengan Bash.

Default Bash menyediakan string kosong pada referensi ke variabel yang tidak ditentukan. CI variabel (atau sesuatu yang analog dalam otomatisasi Anda) dimaksudkan untuk menunjukkan kemungkinan tidak adanya manusia. Sayangnya, untuk robot penguasa, manusia mungkin perlu menjalankan skrip secara manual setidaknya sekali. Pada titik ini, kemungkinan mereka akan lupa menetapkan nilai untuk CI sebelum menekan Enter.

Jadi, kita perlu menetapkan nilai default dan memastikan nilainya selalu benar atau salah. Contoh kode pustaka di atas menunjukkan cara menguji apakah string kosong, dan cara memaksa string berisi salah satu dari sepasang nilai. Cara saya membaca grup pernyataan pertama di anchors.sh adalah:

Tentukan 'CI ' ke depan sebagai akibat dari:

  1. Meneliti nilai CI sebelumnya (mungkin tidak terdefinisi, jadi string kosong).
  2. ':- ' bagian berarti:
    1. Jika nilainya adalah string kosong, nyatakan string 'false ' sebagai gantinya.
    2. Jika nilainya bukan string kosong, gunakan apa pun itu (termasuk 'DaRtH VaDeR ').

Uji benda di dalam '[[] ' dan ' ]] ':

  1. Jika nilai baru 'CI ' sama dengan string literal "false ", buang kode keluar 0 (artinya sukses atau kebenaran).
  2. Jika tidak, buang kode keluar 1 (artinya kegagalan atau ketidakbenaran)

Jika tes keluar dengan 0 , lanjutkan dengan baris berikutnya, atau (' || '), anggap admin pemula mengatur CI=YES!PLEASE atau satu set mesin sempurna CI=true . Segera atur nilai 'CI ' ke string literal 'true '; karena mesin yang sempurna lebih baik, jangan salah.

Untuk sisa pustaka ini, nilai jalur jangkar hampir selalu berguna dalam skrip yang berjalan dalam otomatisasi dari repositori. Saat digunakan dalam proyek yang lebih besar, Anda perlu menyesuaikan lokasi relatif direktori perpustakaan sehubungan dengan root repositori. Jika tidak, saya akan meninggalkan pemahaman pernyataan ini, sebagai latihan penelitian untuk pembaca (lakukan sekarang, itu akan membantu Anda nanti).

Menggunakan pustaka Bash di skrip Bash

Untuk memuat perpustakaan, gunakan source perintah bawaan. Perintah ini tidak mewah. Berikan lokasi file, lalu ia membaca dan mengeksekusi konten di sana dan kemudian, yang berarti bahwa konteks kode perpustakaan run-time sebenarnya akan menjadi skrip yang source itu.

Untuk membantu mencegah terlalu banyak otak Anda menetes keluar dari telinga Anda, berikut adalah ./bin/example.sh sederhana skrip untuk diilustrasikan:

#!/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=')"

Anda mungkin segera menyadari bahwa skrip mengubah konteks run-time sebelum memuat pustaka. Itu juga mendefinisikan LIB_PATH secara lokal dan mengarahkannya ke file (yang membingungkan, bukan direktori) dengan jalur relatif (untuk tujuan ilustrasi).

Silakan jalankan skrip ini dan periksa hasilnya. Perhatikan bahwa semua operasi di perpustakaan anchors.sh berlari di dalam /var/tmp/ direktori dan secara otomatis mengekspor definisinya. Definisi lama untuk LIB_PATH dimusnahkan dan diekspor oleh a di set -ax . Fakta ini terlihat pada output dari declare -x berasal dari export memerintah. Mudah-mudahan, output debugging (x di set -ax ) dapat dimengerti.

Saat men-debug seperti ini, Bash mencetak semua nilai antara saat mem-parsing setiap baris. Saya menyertakan skrip ini di sini untuk menunjukkan mengapa Anda tidak pernah ingin set -ax atau ubah direktori menggunakan perintah dari tingkat teratas perpustakaan. Ingat bahwa instruksi perpustakaan dievaluasi pada waktu buka dalam skrip. Jadi mengubah lingkungan di perpustakaan menyebabkan ada efek samping runtime di skrip apa pun yang digunakan source untuk memuatnya. Efek samping seperti ini dijamin membuat setidaknya satu sysadmin benar-benar gila. Anda tidak pernah tahu, admin itu bisa jadi saya, jadi jangan lakukan itu.

Sebagai contoh praktis, pertimbangkan perpustakaan imajiner yang mendefinisikan fungsi menggunakan variabel lingkungan nama pengguna/kata sandi untuk mengakses layanan jarak jauh. Jika perpustakaan melakukan set -ax tingkat atas sebelum fungsi ini, maka setiap kali dimuat, output debug akan mencakup menampilkan variabel-variabel ini, memerciki rahasia Anda di semua tempat untuk dilihat semua orang. Lebih buruk lagi, akan sulit (dari sudut pandang skrip panggilan), bagi rekan pemula untuk menonaktifkan output tanpa meneriaki keyboard mereka.

Kesimpulannya, kuncinya di sini adalah untuk tetap menyadari bahwa perpustakaan "terjadi" dalam konteks pemanggil mereka. Faktor ini juga mengapa contoh anchors.sh dapat menggunakan  $0   (jalur skrip dan nama file yang dapat dieksekusi), tetapi jalur ke perpustakaan itu sendiri hanya tersedia melalui '${BASH_SOURCE[0]}' "ajaib" (elemen larik). Faktor ini mungkin membingungkan pada awalnya, tetapi Anda harus berusaha untuk tetap disiplin. Hindari perintah yang luas dan menjangkau jauh di perpustakaan. Jika Anda melakukannya, semua karyawan admin baru akan bersikeras untuk membayar donat Anda.

Menulis pengujian unit untuk perpustakaan

Menulis tes unit bisa terasa seperti tugas yang menakutkan sampai Anda menyadari bahwa cakupan yang sempurna biasanya membuang-buang waktu Anda. Namun, merupakan kebiasaan yang baik untuk selalu menggunakan dan memperbarui kode pengujian Anda saat menyentuh kode perpustakaan Anda. Tujuan penulisan tes adalah untuk menangani kasus penggunaan yang paling umum dan jelas dan kemudian melanjutkan. Jangan terlalu memperhatikan kasus sudut atau kurang dari penggunaan sepanjang waktu. Saya juga menyarankan awalnya untuk memfokuskan upaya pengujian perpustakaan Anda pada tingkat pengujian unit daripada pengujian integrasi.

Mari kita lihat contoh lain:skrip yang dapat dieksekusi ./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)

Alasan saya meletakkan skrip ini di ./lib (sebagai lawan dari ./bin ) keduanya tidak nyaman dan karena pengujian tidak boleh bergantung pada penggunaan kode yang mereka periksa. Karena pengujian ini perlu memeriksa jalur, lebih mudah untuk menempatkannya di jalur yang sama dengan perpustakaan. Jika tidak, pendekatan ini adalah masalah preferensi pribadi. Jangan ragu untuk melakukan pengujian sekarang karena mungkin membantu Anda memahami kodenya.

Menutup

Artikel ini sama sekali tidak mewakili keseluruhan penggunaan Bash dalam otomatisasi. Namun, saya mencoba menanamkan pengetahuan dasar dan rekomendasi yang (jika diikuti) pasti akan membuat hidup Anda lebih mudah. Kemudian, bahkan ketika keadaan menjadi sulit, pemahaman yang kuat tentang pentingnya konteks runtime akan berguna.

Akhirnya, scripting di atau untuk otomatisasi bisa menjadi kesalahan yang tak termaafkan. Memiliki unit test dasar di tempat untuk perpustakaan Anda akan membangun kepercayaan diri dan membantu orang berikutnya untuk datang (yang bisa jadi Anda setelah lima tahun lupa). Anda dapat menemukan semua contoh kode yang digunakan dalam artikel ini secara online di sini.

Tertarik untuk memoles dasar-dasar Bash Anda? Lihat:

  • Cara memprogram dengan Bash:Operator logika dan ekspansi shell
  • 5 cara untuk meningkatkan skrip Bash Anda
  • Memulai skrip shell
  • 10 perintah dasar Linux yang perlu Anda ketahui
  • Tips dan trik variabel lingkungan Linux

[ Ingin mencoba Red Hat Enterprise Linux? Unduh sekarang secara gratis. ]


Linux
  1. Penanganan kesalahan dalam skrip Bash

  2. Menggunakan Perintah Tidur Linux di Skrip Bash

  3. mengotomatiskan sesi telnet menggunakan skrip bash

  1. Menggunakan alat SS untuk pemecahan masalah jaringan

  2. Gunakan Ekstensi .sh Atau .bash Untuk Skrip Bash?

  3. Menggunakan Perintah Nama Dasar Linux di Skrip Bash

  1. Bash Untuk Loop

  2. Menulis Komentar di Bash Script

  3. Skrip Bash:Menggunakan perintah skrip dari skrip bash untuk mencatat sesi