Alur eksekusi perintah Bash: operator, alias, dan variabel lingkungan

Bash membaca perintah, mengolah argumen, melakukan ekspansi dan pengalihan, lalu menjalankan perintah tersebut. Tanda titik koma ; melanjutkan ke perintah berikutnya apa pun hasil sebelumnya, && melanjutkan hanya setelah keberhasilan, sedangkan || melanjutkan setelah status bukan nol. Alias mengganti teks perintah saat shell membacanya; variabel lingkungan meneruskan pengaturan ke proses anak. Perbedaan ini menjelaskan mengapa baris perintah yang tampak mirip dapat berperilaku berbeda.

Untuk awal, pelajari variabel shell, variabel lingkungan, alias, status keluar, dan alur dasar &&/||. Kelompok perintah, pipeline, pekerjaan latar belakang, serta skrip lengkap merupakan pembahasan lanjutan yang dapat dibaca setelahnya. Panduan skrip Bash pertama berbahasa Inggris menerapkan aturan ini dalam sebuah berkas; panduan penanganan galat Bash berbahasa Inggris membahas batasan kegagalan, jalur, dan pekerjaan yang berjalan bersamaan.

Bagaimana shell menentukan perintah yang dijalankan?

Contoh dalam panduan ini menggunakan Bash. Shell membaca baris perintah, menangani argumen, variabel, dan pengalihan, lalu menentukan arti nama perintah. Alias, fungsi, perintah bawaan shell, dan program yang ditemukan melalui PATH dapat menghasilkan perilaku berbeda. Periksa cara shell saat ini mengenali sebuah nama dengan:

type -a printf
type -a ls
command -v ls

type -a dapat menampilkan alias, fungsi, perintah bawaan, dan kandidat program eksternal. command -v memeriksa apakah nama dapat dikenali dan menunjukkan hasil penentuannya. PATH berisi direktori yang dicari untuk program eksternal; alias atau fungsi tidak selalu dapat dijelaskan sebagai satu jalur berkas. Jangan menimpa PATH sistem untuk latihan ini.

Membaca status keluar dan simbol eksekusi

Perintah meninggalkan status keluar, yaitu angka yang menyatakan hasil eksekusinya. Nol berarti berhasil; nilai bukan nol berarti kegagalan atau hasil lain yang ditetapkan oleh perintah tersebut. Misalnya, grep dapat mengembalikan 1 ketika tidak ada kecocokan. Baca $? segera atau simpan nilainya dalam variabel, karena perintah berikutnya mengganti status terakhir itu.

true
printf 'status=%s\n' "$?"
false
printf 'status=%s\n' "$?"

Keluarannya adalah status=0, kemudian status=1. Eksekusi bersyarat memakai status, bukan kesan berhasil dari teks pesan. Bash mengembalikan 127 jika perintah tidak ditemukan dan 126 jika ditemukan tetapi tidak dapat dijalankan. Proses yang dihentikan oleh sinyal mempunyai konvensi status tersendiri; jangan menganggap setiap kegagalan menghasilkan 1.

Simbol Kegunaan
; Menjalankan perintah berikutnya terlepas dari keberhasilan atau kegagalan sebelumnya
&& Menjalankan perintah berikutnya hanya setelah status berhasil
|| Menjalankan perintah berikutnya hanya setelah status bukan nol
& Menjalankan secara asinkron tanpa menunggu selesai
| Menghubungkan keluaran standar ke masukan standar perintah berikutnya
|& Meneruskan keluaran standar dan kesalahan standar melalui pipa
! Membalik nilai logis status perintah atau pipeline

Dokumentasi daftar perintah Bash menggolongkan ;, &, &&, dan || sebagai operator daftar. Tanda | dan |& membentuk pipeline, yaitu perintah-perintah yang aliran datanya dihubungkan, sedangkan ! merupakan kata khusus yang menegasikan status pipeline. Jalankan latihan dalam Bash dengan true, false, printf, grep, cat, dan sleep, yang biasanya tersedia di Ubuntu dan Rocky Linux. Hak administrator tidak diperlukan.

Menghubungkan perintah berdasarkan keberhasilan atau kegagalan

Membedakan urutan biasa dan eksekusi bersyarat

Tanda ; menunggu perintah sebelumnya selesai, lalu melanjutkan tanpa memedulikan hasilnya. Baris baru juga memisahkan perintah dalam daftar biasa. Namun, tanda kutip yang belum ditutup, garis miring terbalik untuk menyambung baris, pipeline, atau perintah majemuk yang belum lengkap dapat membuat Bash terus membaca, bukan menganggap baris baru sebagai akhir perintah.

false; printf 'always\n'
false && printf 'not run\n'
false || printf 'fallback\n'
true && printf 'next step\n'

Hasilnya adalah always, fallback, lalu next step; not run tidak dicetak. Operator && dan || memakai evaluasi hubung singkat: sisi kanan dilewati jika syaratnya tidak terpenuhi. Pola cd /actual/work/path && work_command mencegah pekerjaan berjalan di direktori yang salah ketika perpindahan direktori gagal. Jalur dan nama perintah tersebut hanya ilustrasi; ganti dengan nilai nyata yang telah disiapkan.

Membedakan urutan evaluasi dan cabang if

Operator && dan || memiliki prioritas yang sama dan dievaluasi dari kiri ke kanan. Dalam A && B || C, C tetap dapat dijalankan ketika A berhasil, sebab B dapat gagal.

true && false || printf 'fallback runs\n'

Contoh itu mencetak fallback runs. Gunakan if jika hanya kondisi awal yang seharusnya menentukan salah satu dari dua cabang.

if true; then
  false
else
  printf 'condition failed\n'
fi

Tidak ada keluaran: false yang gagal dalam cabang then yang telah dipilih tidak memindahkan eksekusi ke else. Dalam skrip produksi, kegagalan itu tetap perlu ditangani dengan sengaja. Pemilihan cabang yang benar tidak memulihkan pekerjaan yang gagal di dalamnya.

Melaporkan kegagalan tanpa kehilangan status aslinya

false || printf 'failure reported\n'
printf 'list status=%s\n' "$?"

Hasilnya adalah failure reported, kemudian list status=0. Operator || sendiri tidak berarti mengabaikan galat, tetapi printf yang berhasil menjadi perintah terakhir yang dijalankan sehingga daftar itu berstatus nol. Jika skrip harus tetap gagal setelah melaporkan masalah, simpan status kegagalan aslinya dan teruskan dengan exit, seperti dalam skrip lengkap di bawah.

Mengelompokkan perintah menjadi satu kesatuan

Kurung kurawal berjalan dalam shell saat ini

Bentuk { ...; } mengelompokkan perintah dalam shell saat ini. Pisahkan kurung kurawal dari perintah dengan spasi, dan letakkan titik koma atau baris baru sebelum kurung penutup.

LESSON_SCOPE=outer
{ LESSON_SCOPE=inner; printf '%s\n' "$LESSON_SCOPE"; }
printf '%s\n' "$LESSON_SCOPE"
unset LESSON_SCOPE

Contoh ini mencetak inner dua kali, karena perubahan variabel tetap berlaku dalam shell saat ini. Perintah unset terakhir menghapus variabel latihan.

Kurung biasa menggunakan subshell

LESSON_SCOPE=outer
( LESSON_SCOPE=inner; printf '%s\n' "$LESSON_SCOPE" )
printf '%s\n' "$LESSON_SCOPE"
unset LESSON_SCOPE

Hasilnya adalah inner, kemudian outer. Kurung biasa membuat lingkungan subshell, yaitu lingkungan eksekusi turunan, sehingga penetapan variabel biasa dan perubahan direktori di dalamnya tidak bertahan dalam shell luar. Namun, subshell tidak membatalkan efek eksternal seperti perubahan berkas atau permintaan ke layanan. Perbedaannya dijelaskan dalam dokumentasi pengelompokan perintah Bash.

Menggunakan pipa, kesalahan standar, dan pembalikan status

Membedakan pipa dan kondisi

printf 'alpha\nbeta\n' | grep 'alpha'

Hanya alpha yang dicetak. Tanda | menghubungkan keluaran standar (stdout) perintah sebelumnya ke masukan standar (stdin) perintah berikutnya. Pipa tidak memilih perintah setelah kegagalan seperti ||. Perintah dalam pipeline memproses aliran yang terhubung, bukan rangkaian langkah yang masing-masing menunggu syarat keberhasilan tahap sebelumnya.

Menyertakan kesalahan standar dengan |&

bash -c 'printf "normal\n"; printf "error\n" >&2' |& cat

Baik normal maupun error melewati cat. Pada contoh ini, |& milik Bash menggabungkan kesalahan standar (stderr) dengan stdout seperti 2>&1 |. Jika ada pengalihan lain, urutannya juga memengaruhi hasil; jangan mengganti sintaks tanpa memeriksanya. Perintah tee pada tahap berikutnya dapat sekaligus menampilkan dan mencatat aliran, tetapi kedua jenis keluaran tersebut sudah tergabung.

Tanda ! membalik status, bukan keluaran

! false
printf 'inverted status=%s\n' "$?"

Contoh ini mencetak inverted status=0: kegagalan false dinegasikan menjadi keberhasilan untuk pengujian kondisi. Status asal nol berubah menjadi status bukan nol. Teks keluaran maupun isi berkas tidak dibalik.

Memeriksa kegagalan pipeline dengan pipefail

( set +o pipefail; false | cat; printf 'default=%s\n' "$?" )
( set -o pipefail; false | cat; printf 'pipefail=%s\n' "$?" )

Hasilnya adalah default=0, kemudian pipefail=1. Secara bawaan, pipeline mengembalikan status perintah terakhir sehingga cat yang berhasil dapat menutupi false sebelumnya. Ketika pipefail aktif, statusnya nol hanya jika semua perintah berhasil; jika tidak, status diambil dari perintah paling kanan yang gagal. Kurung biasa mengisolasi kedua percobaan agar tidak mengubah opsi shell luar. Dokumentasi pipeline Bash menjelaskan aturan ini.

Memulai pekerjaan latar belakang dan memeriksa penyelesaiannya

Tanda & memulai eksekusi asinkron tanpa menunggu pekerjaan selesai. Ini berbeda dari &&, yang hanya melanjutkan setelah keberhasilan. Contoh berikut memulai pekerjaan singkat di latar belakang, lalu sengaja menunggu penyelesaiannya.

sleep 2 &
lesson_job=$!
printf 'foreground continues\n'
wait "$lesson_job"
printf 'job status=%s\n' "$?"
unset lesson_job

foreground continues muncul lebih dahulu, disusul job status=0 setelah pekerjaan selesai. Nilai $! mengidentifikasi proses latar belakang terbaru. Nilai $? tepat setelah peluncurannya belum membuktikan apakah pekerjaan akhirnya berhasil. Bash interaktif menyediakan jobs, fg, dan bg untuk memeriksa serta memindahkan pekerjaan. Menambahkan & saja tidak menjamin proses tetap hidup setelah keluar dari sesi; gunakan pengelolaan sesi atau layanan yang sesuai untuk pekerjaan lama.

Mempraktikkan alur dengan skrip lengkap

Contoh lengkap ini menyimulasikan build, pengujian, dan penerapan melalui pesan. Tidak ada build, penerapan, atau penghapusan berkas yang benar-benar dilakukan. Simpan sebagai berkas baru bernama course15-flow.sh dalam direktori latihan kosong. Hanya Bash yang diperlukan; berkas tidak perlu diberi izin eksekusi karena dijalankan sebagai masukan untuk bash.

#!/usr/bin/env bash
build() { printf 'build\n'; }
run_tests() { printf 'test\n'; return "${1:-0}"; }
deploy() { printf 'deploy\n'; }

build && run_tests "${1:-0}" && deploy
lesson_status=$?
if (( lesson_status != 0 )); then
  printf 'workflow failed: status=%s\n' "$lesson_status" >&2
fi
exit "$lesson_status"
bash course15-flow.sh 0
echo $?
bash course15-flow.sh 1
echo $?

Eksekusi pertama mencetak build, test, dan deploy, lalu mengembalikan 0. Mengubah argumen menjadi 1 hanya mencetak build dan test, melaporkan kegagalan melalui stderr, dan mengembalikan 1. Jalankan kembali dengan argumen 0 untuk kembali ke jalur berhasil. Bentuk ${1:-0} memakai nol jika argumen pertama tidak diberikan atau kosong; gunakan hanya nol atau satu dalam latihan ini.

Fungsi dengan bentuk nama() { ...; } memberi nama pada kelompok perintah; return menentukan statusnya. Menyimpan status alur segera dalam lesson_status mencegah pesan pelaporan mengganti kegagalan asli. Dalam pekerjaan nyata, gantilah simulasi dengan tugas yang telah disiapkan dan divalidasi, serta tangani setiap kegagalan secara eksplisit.

Gunakan titik koma untuk langkah singkat yang independen, && untuk pekerjaan yang bergantung pada keberhasilan, serta if dan fungsi untuk percabangan dan pemulihan. Pola work; cleanup menjalankan cleanup setelah kegagalan biasa pada work, tetapi tidak menjaminnya ketika shell keluar lebih awal atau menerima sinyal. Rancang pembersihan seperti itu dengan fasilitas semacam trap. Demikian pula, set -e tidak otomatis menangani semua galat: kondisi dan daftar AND/OR mempunyai pengecualian, sehingga status penting perlu diperiksa secara eksplisit.

Alias diekspansi saat Bash membaca perintah

alias membuat nama pendek untuk perintah, biasanya digunakan dalam Bash interaktif. Masukkan definisi berikut, lalu jalankan ll atau type ll pada prompt baru. Bash membaca dan mengurai perintah sebelum menjalankannya, sehingga mendefinisikan dan menggunakan alias dalam perintah majemuk yang sama dapat membuat ekspansi tidak terjadi seperti yang diharapkan.

alias ll='ls -l'
type ll
ll

Alias biasanya hanya memengaruhi shell saat ini dan tidak diekspansi dalam Bash noninteraktif kecuali diaktifkan secara terpisah. Jangan sampai skrip otomatis tanpa sengaja bergantung pada alias interaktif. Hapus alias latihan dengan unalias ll. Untuk operasi berulang yang memerlukan argumen, pertimbangkan fungsi shell sebagai pengganti alias.

Membedakan variabel shell dan variabel lingkungan

Variabel shell tersedia dalam shell saat ini. Setelah diekspor, variabel juga dimasukkan ke lingkungan, yaitu kumpulan nilai yang diwariskan ke proses anak yang diluncurkan sesudahnya. Gunakan nama khusus latihan, bukan nama variabel sistem yang umum:

LESSON_MODE=practice
printf '%s\n' "$LESSON_MODE"
export LESSON_MODE
printenv LESSON_MODE

Kedua keluarannya adalah practice. Yang pertama berasal dari ekspansi dalam shell saat ini; yang kedua dibaca dari lingkungan yang diteruskan ke proses eksternal printenv. Mengekspor variabel tidak mengubah secara surut shell lain yang sudah berjalan atau proses induk.

Untuk memasok nilai hanya kepada satu perintah, gunakan pola NAME=value command dengan nama variabel dan perintah yang sesuai. Setelah selesai, jalankan unset LESSON_MODE dalam shell saat ini. Jangan menjadikan variabel direktori beranda atau PATH sistem sebagai variabel latihan.

Menelusuri masalah urutan eksekusi

  • Jika belum tahu bagaimana suatu nama dikenali, mulai dengan type -a nama, mengganti nama dengan perintah yang diperiksa.
  • Jika eksekusi bersyarat mengejutkan, periksa status keluar tiap tahap secara terpisah.
  • Jika alias tidak bekerja, periksa apakah Bash interaktif dan apakah alias digunakan pada baris perintah baru setelah didefinisikan.
  • Jika proses anak tidak melihat variabel, periksa apakah variabel sudah diekspor.

Operator urutan, alias, dan variabel sama-sama memengaruhi eksekusi, tetapi fungsinya berbeda. Sebelum merangkai perintah yang mengubah berkas, uji alurnya dengan perintah bawaan berdampak kecil seperti true, false, dan printf.

Dokumentasi resmi

Dokumentasi daftar perintah GNU Bash membahas ;, &&, dan ||. Dokumentasi alias Bash menjelaskan waktu ekspansinya. Untuk variabel dan perilaku lingkungan, lihat Bash Reference Manual.

Aturan terperinci tersedia dalam dokumentasi GNU Bash tentang pipeline dan pipefail, pengelompokan perintah, serta status keluar.