Saya sedang mengerjakan skrip shell yang membangun perintah kompleks dari variabel, misalnya seperti ini (dengan teknik yang saya pelajari dari Bash FAQ ):
#!/bin/bash
SOME_ARG="abc"
ANOTHER_ARG="def"
some_complex_command \
${SOME_ARG:+--do-something "$SOME_ARG"} \
${ANOTHER_ARG:+--with "$ANOTHER_ARG"}
Script ini secara dinamis menambahkan parameter --do-something "$SOME_ARG"
dan --with "$ANOTHER_ARG"
untuk some_complex_command
jika variabel ini didefinisikan. Sejauh ini ini berfungsi dengan baik.
Tetapi sekarang saya juga ingin dapat mencetak atau mencatat perintah ketika saya menjalankannya, misalnya ketika skrip saya dijalankan dalam mode debug. Jadi ketika skrip saya berjalan some_complex_command --do-something abc --with def
, saya juga ingin memiliki perintah ini di dalam suatu variabel sehingga saya dapat misalnya log ke syslog.
Bash FAQ menunjukkan teknik untuk menggunakan DEBUG
jebakan dan $BASH_COMMAND
variabel (misalnya untuk tujuan debugging) untuk tujuan ini. Saya sudah mencobanya dengan kode berikut:
#!/bin/bash
ARG="test string"
trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG"
echo "Command was: ${COMMAND}"
Ini berfungsi, tetapi itu tidak memperluas variabel dalam perintah:
host ~ # ./test.sh
test string
Command was: echo "$ARG"
Saya kira saya harus menggunakan eval untuk memperluas echo "$ARG"
ke echo test string
(setidaknya saya belum menemukan cara tanpa eval
belum). Berikut ini berfungsi:
eval echo "Command was: ${COMMAND}"
Ini menghasilkan output berikut:
host ~ # ./test.sh
test string
Command was: echo "$ARG"
Command was: echo test string
Tapi saya tidak begitu yakin apakah saya bisa menggunakannya eval
dengan aman seperti ini. Saya gagal mencoba mengeksploitasi beberapa hal:
#!/bin/bash
ARG="test string; touch /x"
DANGER='$(touch /y; cat /etc/shadow)'
trap 'COMMAND="$BASH_COMMAND"; trap - DEBUG' DEBUG
echo "$ARG" $DANGER
echo "Command was: ${COMMAND}"
eval echo "Command was: ${COMMAND}"
Tampaknya untuk menangani ini dengan baik, tetapi saya ingin tahu apakah ada orang lain melihat masalah yang saya lewatkan.
Jawaban:
Salah satu kemungkinan adalah membuat fungsi pembungkus yang pada saat yang sama akan mencetak perintah dan menjalankannya, sebagai berikut:
Sehingga dalam skrip Anda dapat Anda lakukan:
Tidak perlu mengutak-atik perangkap. Kekurangannya adalah ia menambahkan beberapa
debug
kata kunci di seluruh kode Anda (tapi itu harus baik-baik saja, itu umum untuk memiliki hal-hal seperti itu, seperti menegaskan, dll).Anda bahkan dapat menambahkan variabel global
DEBUG
dan memodifikasidebug
fungsi seperti:Kemudian Anda dapat memanggil skrip Anda sebagai:
atau
atau hanya
tergantung apakah Anda ingin memiliki informasi debug atau tidak.
Saya mengkapitalkan
DEBUG
variabel karena harus diperlakukan sebagai variabel lingkungan.DEBUG
adalah nama yang sepele dan umum, jadi ini mungkin berbenturan dengan perintah lain. Mungkin menyebutnyaGNIOURF_DEBUG
atauMARTIN_VON_WITTICH_DEBUG
atauUNICORN_DEBUG
jika Anda suka unicorn (dan kemudian Anda mungkin suka kuda juga).Catatan. Dalam
debug
fungsi ini, saya dengan hati-hati memformat setiap argumen denganprintf '%q'
sehingga output akan keluar dengan benar dan dikutip sehingga dapat digunakan kembali kata demi kata dengan copy dan paste langsung. Ini juga akan menunjukkan dengan tepat apa yang dilihat shell karena Anda akan dapat mengetahui setiap argumen (jika spasi atau simbol lucu lainnya). Fungsi ini juga menggunakan penugasan langsung dengan-v
saklarprintf
untuk menghindari subkulit yang tidak perlu.sumber
&
. Saya harus memindahkannya ke fungsi agar berfungsi - tidak terlalu bagus, tapi saya rasa tidak ada cara yang lebih baik. Tapi tidak lebiheval
, jadi saya punya itu untuk saya, yang bagus :)eval "$BASH_COMMAND"
menjalankan perintah.printf '%s\n' "$BASH_COMMAND"
mencetak perintah yang ditentukan dengan tepat, ditambah baris baru.Jika perintah berisi variabel (yaitu jika itu seperti
cat "$foo"
), maka mencetak perintah akan mencetak teks variabel. Tidak mungkin untuk mencetak nilai variabel tanpa menjalankan perintah - pikirkan perintah sepertivariable=$(some_function) other_variable=$variable
.Cara paling sederhana untuk mendapatkan jejak dari menjalankan skrip shell adalah dengan mengatur
xtrace
opsi shell dengan menjalankan skrip sebagaibash -x /path/to/script
atau memanggilset -x
di dalam shell. Jejak dicetak ke kesalahan standar.sumber
xtrace
, tapi itu tidak memberi saya banyak kendali. Saya mencoba "set -x; ...; set + x", tetapi: 1) perintah "set + x" untuk menonaktifkan xtrace dicetak juga 2) Saya tidak dapat mengawali output misalnya dengan stempel waktu 3) Saya bisa dapat login ke syslog.BASH_COMMAND
yang berisi perintah yang diperluas, karena pada titik tertentu tetap harus melakukan ekspansi variabel pada perintah ketika menjalankannya :)