Menurut manual Bash , variabel lingkungan BASH_COMMAND
berisi
Perintah saat ini sedang dieksekusi atau akan dieksekusi, kecuali shell mengeksekusi perintah sebagai hasil jebakan, dalam hal ini adalah perintah yang dieksekusi pada saat jebakan.
Mengambil kasus sudut perangkap itu, jika saya mengerti dengan benar ini berarti bahwa ketika saya menjalankan perintah, variabel BASH_COMMAND
berisi perintah itu. Tidak sepenuhnya jelas apakah variabel itu tidak disetel setelah eksekusi perintah (yaitu, hanya tersedia ketika perintah sedang berjalan, tetapi tidak setelah), meskipun orang mungkin berpendapat bahwa karena itu adalah "perintah saat ini sedang dieksekusi atau akan dieksekusi" , itu bukan perintah yang baru saja dieksekusi.
Tapi mari kita periksa:
$ set | grep BASH_COMMAND=
$
Kosong. Saya akan berharap untuk melihat BASH_COMMAND='set | grep BASH_COMMAND='
atau mungkin adil BASH_COMMAND='set'
, tetapi kosong mengejutkan saya.
Mari kita coba yang lain:
$ echo $BASH_COMMAND
echo $BASH_COMMAND
$
Itu masuk akal. Saya menjalankan perintah echo $BASH_COMMAND
dan variabel tersebut BASH_COMMAND
berisi string echo $BASH_COMMAND
. Mengapa ini bekerja saat ini, tetapi tidak sebelumnya?
Mari kita lakukan set
lagi:
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$
Jadi, tunggu. Hal itu ditetapkan ketika saya dieksekusi bahwa echo
perintah, dan itu tidak diset setelah itu. Tetapi ketika saya dieksekusi set
lagi, BASH_COMMAND
tidak diatur ke set
perintah. Tidak peduli seberapa sering saya menjalankan set
perintah di sini, hasilnya tetap sama. Jadi, apakah variabel ditetapkan ketika menjalankan echo
, tetapi tidak ketika menjalankan set
? Ayo lihat.
$ echo Hello AskUbuntu
Hello AskUbuntu
$ set | grep BASH_COMMAND=
BASH_COMMAND='echo $BASH_COMMAND'
$
Apa? Jadi variabel ditetapkan ketika saya dieksekusi echo $BASH_COMMAND
, tetapi tidak ketika saya dieksekusi echo Hello AskUbuntu
? Di mana bedanya sekarang? Apakah variabel hanya disetel ketika perintah saat ini sendiri benar-benar memaksa shell untuk mengevaluasi variabel? Mari kita coba sesuatu yang berbeda. Mungkin beberapa perintah eksternal kali ini, bukan bash builtin, untuk suatu perubahan.
$ /bin/echo $BASH_COMMAND
/bin/echo $BASH_COMMAND
$ set | grep BASH_COMMAND=
BASH_COMMAND='/bin/echo $BASH_COMMAND'
$
Hmm, ok ... lagi, variabelnya sudah diset. Jadi, apakah tebakan saya saat ini benar? Apakah variabel hanya ditetapkan ketika harus dievaluasi? Mengapa? Mengapa? Untuk alasan kinerja? Mari kita coba sekali lagi. Kami akan mencoba grep untuk $BASH_COMMAND
dalam file, dan karena itu $BASH_COMMAND
harus mengandung grep
perintah, grep
harus grep untuk grep
perintah itu (yaitu, untuk dirinya sendiri). jadi mari kita buat file yang sesuai:
$ echo -e "1 foo\n2 grep\n3 bar\n4 grep \$BASH_COMMAND tmp" > tmp
$ grep $BASH_COMMAND tmp
grep: $BASH_COMMAND: No such file or directory
tmp:2 grep <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp <-- here, the word "grep" is RED
tmp:2 grep <-- here, the word "grep" is RED
tmp:4 grep $BASH_COMMAND tmp <-- here, the word "grep" is RED
$ set | grep BASH_COMMAND=
BASH_COMMAND='grep --color=auto $BASH_COMMAND tmp'
$
Ok, menarik. Perintah grep $BASH_COMMAND tmp
menjadi diperluas ke grep grep $BASH_COMMAND tmp tmp
(variabel akan diperluas hanya sekali, tentu saja), dan jadi saya mengerti grep
, sekali dalam file $BASH_COMMAND
yang tidak ada, dan dua kali dalam file tmp
.
T1: Apakah asumsi saya saat ini benar bahwa:
BASH_COMMAND
hanya diset ketika sebuah perintah mencoba untuk benar-benar mengevaluasinya; dan- itu tidak diset setelah pelaksanaan perintah, meskipun deskripsi dapat membawa kita untuk percaya begitu?
T2: Jika ya, mengapa? Performa? Jika tidak, bagaimana lagi perilaku dalam urutan perintah di atas dapat dijelaskan?
T3: Terakhir, apakah ada skenario di mana variabel ini sebenarnya bisa digunakan secara bermakna? Saya benar-benar mencoba menggunakannya di dalam $PROMPT_COMMAND
untuk menganalisis perintah yang sedang dieksekusi (dan melakukan beberapa hal tergantung pada itu), tetapi saya tidak bisa, karena begitu, dalam saya $PROMPT_COMMAND
, saya menjalankan perintah untuk melihat variabel $BASH_COMMAND
, variabel mendapat set ke perintah itu. Bahkan ketika saya melakukan yang MYVARIABLE=$BASH_COMMAND
benar di awal saya $PROMPT_COMMAND
, kemudian MYVARIABLE
berisi string MYVARIABLE=$BASH_COMMAND
, karena tugas juga merupakan perintah. (Pertanyaan ini bukan tentang bagaimana saya bisa mendapatkan perintah saat ini dalam $PROMPT_COMMAND
eksekusi. Ada cara lain, saya tahu.)
Ini agak mirip dengan prinsip ketidakpastian Heisenberg. Hanya dengan mengamati variabelnya, saya mengubahnya.
sumber
bash
über- gurus yang asli di sana.Jawaban:
Menjawab pertanyaan ketiga: tentu saja dapat digunakan secara bermakna dengan cara manual Bash secara jelas mengisyaratkan - dalam perangkap, misalnya:
sumber
$BASH_COMMAND
memang dapat digunakan secara bermakna bahkan dalam konteks selain jebakan. Namun, penggunaan yang Anda gambarkan mungkin yang paling khas dan mudah. Jadi jawaban Anda, dan yang oleh DocSalvager, jawab Q3 terbaik saya , dan ini yang paling penting bagi saya. Adapun Q1 dan Q2, seperti yang ditunjukkan oleh @zwets, hal-hal itu tidak terdefinisi. Mungkin$BASH_COMMAND
hanya diberi nilai jika diakses, untuk kinerja - atau beberapa kondisi program internal yang aneh dapat menyebabkan perilaku itu. Siapa tahu?$BASH_COMMAND
untuk selalu ditetapkan, dengan memaksa evaluasi$BASH_COMMAND
sebelum setiap perintah. Untuk melakukan ini kita dapat menggunakan perangkap DEBUG, yang dieksekusi sebelum setiap perintah (semua em). Jika kita mengeluarkan, misalnya,trap 'echo "$BASH_COMMAND" > /dev/null' DEBUG
maka$BASH_COMMAND
akan selalu dievaluasi sebelum setiap perintah (dan diberi nilai itu$COMMAND
). Jadi jika saya mengeksekusiset | grep BASH_COMMAND=
saat jebakan itu aktif ... apa yang Anda ketahui? Sekarang hasilnyaBASH_COMMAND=set
, seperti yang diharapkan :)Sekarang bahwa Q3 telah dijawab (dengan benar, menurut pendapat saya:
BASH_COMMAND
berguna dalam perangkap dan hampir tidak di tempat lain), mari kita coba Q1 dan Q2.Jawaban untuk Q1 adalah: kebenaran asumsi Anda tidak dapat diputuskan. Kebenaran dari poin-poin ini tidak dapat ditentukan, ketika mereka bertanya tentang perilaku yang tidak ditentukan. Dengan spesifikasinya, nilai
BASH_COMMAND
diatur ke teks perintah selama durasi eksekusi perintah itu. Spec tidak menyatakan berapa nilainya harus dalam situasi lain, yaitu ketika tidak ada perintah yang dijalankan. Itu bisa memiliki nilai atau tidak sama sekali.Jawaban untuk Q2 "Jika tidak, bagaimana lagi perilaku dalam urutan perintah di atas dapat dijelaskan?" kemudian mengikuti secara logis (jika agak pedantik): dijelaskan oleh fakta bahwa nilai
BASH_COMMAND
tidak terdefinisi. Karena nilainya tidak terdefinisi, nilainya dapat memiliki nilai apa pun, yang persis seperti yang ditunjukkan urutannya.Nota bene
Ada satu titik di mana saya pikir Anda benar-benar memukul titik lemah di spec. Itu tempat Anda mengatakan:
Cara saya membaca halaman bash man, bit dalam huruf miring tidak benar. Bagian ini
SIMPLE COMMAND EXPANSION
menjelaskan bagaimana tugas variabel pertama pada baris perintah disisihkan, dan kemudianIni menunjukkan kepada saya bahwa tugas variabel bukan perintah (dan karena itu dibebaskan dari muncul di
BASH_COMMAND
), seperti dalam bahasa pemrograman lain. Ini kemudian juga akan menjelaskan mengapa tidak ada garisBASH_COMMAND=set
dalam outputset
,set
pada dasarnya menjadi 'penanda' sintaksis untuk penugasan variabel.OTOH, dalam paragraf terakhir dari bagian itu dikatakan
... yang menyarankan sebaliknya, dan penugasan variabel juga merupakan perintah.
sumber
set
saya harus melihatBASH_COMMAND='set'
di suatu tempat di output itu, yangset
perintah . Oleh karena itu, menurut spesifikasi itu harus berfungsi. Jadi apakah implementasinya tidak setia mematuhi spesifikasi, atau apakah saya salah paham spesifikasi? Menemukan jawaban untuk pertanyaan itu adalah semacam tujuan dari Q1. +1 untukset
.set
bukan "perintah". Sama seperti=
bukan "perintah". Pemrosesan teks yang mengikuti / mengelilingi token tersebut dapat memiliki logika yang sepenuhnya berbeda dari pemrosesan "perintah."set
tidak dihitung sebagai "perintah". Saya tidak akan mengira itu$BASH_COMMAND
akan berubah menjadi terlalu rendah dalam banyak situasi. Saya kira setidaknya kita telah menetapkan bahwa halaman manual pasti bisa lebih jelas tentang hal itu;)Penggunaan inventif untuk $ BASH_COMMAND
Baru-baru ini menemukan penggunaan $ BASH_COMMAND yang mengesankan dalam mengimplementasikan fungsionalitas seperti makro.
Artikel penulis sebelumnya juga memberikan latar belakang yang baik saat menerapkan teknik menggunakan DEBUG
trap
. Thetrap
dihilangkan dalam versi perbaikan.sumber
$BASH_COMMAND
dapat digunakan secara bermakna dalam konteks selain atrap
. Dan apa gunanya itu! Menggunakannya untuk mengimplementasikan perintah makro di bash - siapa yang mengira itu mungkin? Terima kasih untuk penunjuknya.Penggunaan yang tampaknya umum untuk jebakan ... debug adalah untuk meningkatkan judul yang muncul di daftar jendela (^ A ") ketika Anda menggunakan" layar ".
Saya mencoba membuat "layar", "daftar jendela" lebih bermanfaat sehingga saya mulai menemukan artikel yang merujuk "perangkap ... debug".
Saya menemukan bahwa metode menggunakan PROMPT_COMMAND untuk mengirim "urutan judul nol" tidak berfungsi dengan baik, jadi saya kembali ke perangkap ... metode debug.
Inilah cara Anda melakukannya:
1 Beri tahu "layar" untuk mencari urutan keluar (aktifkannya) dengan memasukkan "shelltitle '$ | bash:'" ke "$ HOME / .screenrc".
2 Matikan propagasi perangkap debug menjadi sub-shell. Ini penting, karena jika itu diaktifkan, semuanya akan dibuang, gunakan: "set + o functrace".
3 Kirim urutan pelarian judul untuk "layar" untuk ditafsirkan: trap 'printf "\ ek $ (tanggal +% Y% m% d% H% M% S) $ (whoami) @ $ (nama host): $ (pwd) $ {BASH_COMMAND} \ e \ "'" DEBUG "
Itu tidak sempurna, tetapi itu membantu, dan Anda dapat menggunakan metode ini untuk benar-benar memasukkan apa pun yang Anda suka ke dalam judul
Lihat "daftar jendela" berikut (5 layar):
Bendera Nama Num
1 bash: 20161115232035 mcb @ ken007: / home / mcb / ken007 RSYNCCMD = "sudo rsync" myrsync.sh -R -r "$ {1}" "$ {BACKUPDIR} $ {2: +" / $ {2} " } "$ 2 bash: 20161115230434 mcb @ ken007: / home / mcb / ken007 ls --color = auto -la $ bash: 20161115230504 mcb @ ken007: / home / mcb / ken007 cat bin / psg.sh $ 4 bash: 20161115222415 mcb @ ken007: / home / mcb / ken007 ssh ken009 $ 5 pesta: 20161115222450 mcb @ ken007: / home / mcb / ken007 mycommoncleanup $
sumber