Sering terjadi bahwa Anda datang ke mesin dan menemukan beberapa jenis skrip berjalan di bawah pengguna sistem seminggu yang lalu. Siapa yang meluncurkannya? Di mana mencari run.php ini? Atau Anda menambahkan entri ke / etc / crontab, dan skrip macet di sana dengan kesalahan "perintah tidak ditemukan". Mengapa Dan apa yang harus dilakukan?
Saya punya jawaban untuk pertanyaan-pertanyaan ini.

Variabel lingkungan
Di hampir semua sistem operasi modern, proses memiliki variabel lingkungan. Secara teknis, mereka adalah kumpulan string bernama. Jika suatu subproses dimulai, maka secara otomatis mewarisi salinan lingkungan induknya.
Antara lain, ada variabel PATH, yang menunjukkan jalur untuk mencari file yang dapat dieksekusi, variabel HOME, yang menunjukkan direktori home pengguna, variabel yang bertanggung jawab untuk preferensi bahasa pengguna, dan banyak lainnya.
Ada banyak ulasan yang menggambarkan arti dari variabel-variabel ini, tetapi praktis tidak ada artikel tentang cara menyelidiki masalah. Isi celah ini.
Siapa yang memulai prosesnya?
Jadi, kami menemukan skrip berjalan di bawah pengguna sistem seminggu yang lalu. Siapa yang meluncurkannya? Mengapa Mungkin mereka baru saja melupakannya? Berpotensi 10-15 orang dapat meluncurkannya, Anda tidak akan mewawancarai semua orang. Bagaimana menemukan siapa orang itu? Dan di mana run.php ini terletak?
$ ps x | grep run.php 10684 ? Ss 472:25 /local/php/bin/php run.php
Variabel lingkungan proses dan fitur sudo datang untuk menyelamatkan. Ada variabel PWD di mana shell menyimpan direktori kerja saat ini; nilai ini, pada kenyataannya, menyimpan informasi tentang direktori saat ini pada saat perintah dijalankan. Juga, utilitas sudo secara default meninggalkan informasi dalam variabel lingkungan proses tentang pengguna yang diluncurkan.
Variabel lingkungan (dan banyak lagi) untuk setiap proses yang berjalan dapat ditemukan di / proc. Voila:
$ cat /proc/10684/environ | tr '\0' '\n' | grep SUDO_USER SUDO_USER=alexxz $ cat /proc/10684/environ | tr '\0' '\n' | grep PWD PWD=/home/etlmaster
Ahem, saya meluncurkannya sendiri. Nah, siapa yang tidak kebetulan?
Secara umum, menggunakan metode sederhana dalam situasi sederhana, Anda dapat menemukan informasi tentang prosesnya, yang umumnya tidak tersedia.
Script bekerja dari baris perintah, tetapi tidak bekerja dari cron
Salah satu kasus ketika Anda harus berpikir tentang variabel lingkungan adalah ketika skrip ditambahkan ke / etc / crontab lumpuh dengan kesalahan. Anda pergi ke server melalui SSH, jalankan perintah, semuanya tampak berfungsi sebagaimana mestinya. Dan ketika mulai secara otomatis, itu menunjukkan sesuatu seperti "sarang: perintah tidak ditemukan".
Secara umum, ini adalah praktik yang baik untuk menulis path lengkap ke perintah yang dapat dieksekusi, tetapi ini tidak selalu mungkin. Dalam kasus seperti itu, pengembang keluar seperti yang bisa dilakukan siapa pun. Seseorang menambahkan jalur yang diinginkan di PATH sebagai bagian dari tim di crontab. Yang lebih berpengalaman membungkus perintah mereka dalam bash -l. Dan bom gagak yang diajarkan oleh pengalaman pahit masih tidak lupa untuk berkumpul. Semuanya begitu: dibuat, ditambahkan ke pemantauan dan dilupakan.
Setelah manipulasi semacam itu, endapan tetap berada dalam jiwa seorang insinyur sejati. Ya, masalah terpecahkan. Tetapi saya tidak mengerti apa yang sedang terjadi! Bagaimana satu pendekatan lebih baik dari yang lain? Di mana semua pengaturan ini disimpan dan oleh siapa mereka diubah?
Mari kita bandingkan variabel lingkungan yang dimiliki proses ketika diluncurkan dari mahkota dan variabel lingkungan yang kita miliki di baris perintah. Kami mencatat output dari perintah env dari mahkota dan lingkungan kita saat ini:
$ echo "* * * * * env > ~/crontab.env" | crontab; sleep 60; echo "" | crontab; $ env > my.env
Lihat apa yang ada di variabel PATH:
> grep ^PATH= crontab.env my.env Crontab.env: PATH=/usr/bin:/bin My.env: PATH=/local/hive/bin:/local/python/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hive/bin:/local/hadoop/bin:/usr/local/bin:/usr/bin:/bin

Mama Mia! Jadi hanya ada minimum di bawah mahkota! Tentu saja, Anda perlu memuat variabel lingkungan normal.
Mari kita lihat seperti apa lingkungannya jika kita menambahkan bash -l:
$ echo "* * * * * bash -l env > ~/crontab.env" | crontab; sleep 60; echo "" | crontab; alexxz@bi1.mlan:~> grep ^PATH= crontab.env my.env Crontab.env: PATH=/local/hive/bin:/local/python/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hive/bin:/usr/local/bin:/usr/bin:/bin My.env: PATH=/local/hive/bin:/local/python/bin:/local/hadoop/bin:/local/hadoop/bin:/local/hive/bin:/local/hadoop/bin:/usr/local/bin:/usr/bin:/bin
Perbedaannya tidak begitu terlihat. Semua jalur disajikan. Beberapa dalam urutan yang berbeda, beberapa diulang, tetapi ini sudah jauh lebih baik daripada sebelumnya. Sisa variabel juga disetel dengan baik. Tentu saja ada sedikit perbedaan dalam lokal, dalam variabel dari SSH, tetapi ini seharusnya tidak lagi secara dramatis mempengaruhi pengoperasian skrip.
Sekarang sudah jelas mengapa bash -l diperlukan dalam entri crontab. Dan, tentu saja, jangan lupa tentang kawanan.
Inisialisasi debug skrip login
Masalahnya tampaknya diselesaikan, semuanya dari mahkota bekerja. Tetapi bagaimana beberapa jalur diduplikasi dalam variabel PATH? Jadi ada beberapa jenis kekacauan dalam pengaturan server. Mari kita coba mencari tahu.
Kami membuka beberapa orang untuk menginisialisasi lingkungan, kami membacakan skrip mana dan dalam urutan apa dijalankan, dengan antusiasme kami mulai mengalir melalui mata mereka - dan setelah beberapa menit perasaan putus asa muncul. Beberapa aliran kondisi tanpa akhir tentang beberapa kasus khusus arsitektur, terminal dan pengaturan warna yang sangat penting untuk perintah ls. Nyeri, putus asa, benci! Kami tertarik pada satu variabel PATH!
Faktanya, semuanya agak sederhana. Bertemu:
env -i bash -x -l -c 'echo 123' > login.log 2>&1
Apa yang dilakukan tim ini? Membuat proses bash baru dengan lingkungan yang murni, menunjukkan bahwa perlu menjalankan skrip inisialisasi dan mengamankan semuanya secara terperinci dalam file login.log. Sekarang kita memiliki kesempatan untuk tidak mengeksekusi semua skrip dalam pikiran kita, tetapi hanya membaca apa, di mana dan kapan dieksekusi dan dari mana pengaturan lingkungan ini atau itu berasal.
Saya tidak akan menganalisis secara detail cara membaca log yang dihasilkan. Semuanya hampir sepele di sana. Saya hanya menyebutkan bahwa satu hit berasal dari / etc / profile dan dua hit dari /etc/bash.bashrc. Ya, di suatu tempat mereka terlalu pintar ketika mengatur paket di pappet. Yah, tidak ada, itu tidak mengganggu saya untuk bekerja.
Tapi sekarang saya tahu dan bisa!
PS Dalam kasus yang sangat sulit dan untuk memahami semuanya, Anda dapat membungkus perintah dalam strace:
strace -f env -i bash -x -l -c 'echo 123' > login.log 2>&1