Bagaimana cara mensimulasikan lingkungan yang dijalankan oleh cron dengan skrip?

253

Saya biasanya memiliki beberapa masalah dengan bagaimana cron mengeksekusi skrip karena mereka biasanya tidak memiliki pengaturan lingkungan saya. Apakah ada cara untuk memanggil bash (?) Dengan cara yang sama dengan cron sehingga saya bisa menguji skrip sebelum menginstalnya?

Jorge Vargas
sumber
Saya akan menyarankan solusi ini: unix.stackexchange.com/questions/27289/…
rudi
Mengambil @gregseth sedikit lebih jauh, saya memberikan solusi ini: unix.stackexchange.com/questions/27289/…
Robert Brisita

Jawaban:

385

Tambahkan ini ke crontab Anda (sementara):

* * * * * env > ~/cronenv

Setelah berjalan, lakukan ini:

env - `cat ~/cronenv` /bin/sh

Ini mengasumsikan bahwa cron Anda menjalankan / bin / sh, yang merupakan default terlepas dari shell default pengguna.

mmccoo
sumber
5
Catatan: jika menambahkan itu ke global / etc / crontab, Anda akan memerlukan nama pengguna juga. Misalnya * * * * * root env> ~ / cronenv
Greg
10
Bagus, ide sederhana. Untuk penggunaan yang tidak sabar '* * * * *' untuk menjalankan menit berikutnya, dan ingat untuk mematikan lagi ketika Anda selesai bermain ;-)
Mads Buus
5
@Madsn Untuk kembali ke bash shell sebelumnya coba: exit
spkane
5
Pentingnya jawaban ini tidak bisa diremehkan. Layak untuk dimasukkannya paragraf dalam sebuah buku.
Xofo
1
Apakah env - cat ~ / cronenv` / bin / sh` harus ditulis sebagai cron job juga? tolong beri contoh
JavaSa
61

Cron hanya menyediakan lingkungan ini secara default:

  • HOME direktori home pengguna
  • LOGNAME login pengguna
  • PATH=/usr/bin:/usr/sbin
  • SHELL=/usr/bin/sh

Jika Anda membutuhkan lebih banyak, Anda dapat sumber skrip di mana Anda mendefinisikan lingkungan Anda sebelum tabel penjadwalan di crontab.

gregseth
sumber
7
.biasanya bukan bagian dari PATHlagi, untuk alasan keamanan .
l0b0
49

Beberapa pendekatan:

  1. Ekspor cron env dan sumberkan:

    Menambahkan

    * * * * * env > ~/cronenv

    ke crontab Anda, biarkan berjalan sekali, matikan kembali, lalu jalankan

    env - `cat ~/cronenv` /bin/sh

    Dan Anda sekarang berada di dalam shsesi yang memiliki lingkungan cron

  2. Bawa lingkungan Anda ke cron

    Anda dapat melewati latihan di atas dan hanya melakukan . ~/.profiledi depan pekerjaan cron Anda, misalnya

    * * * * * . ~/.profile; your_command
  3. Gunakan layar

    Dua solusi di atas masih gagal karena mereka menyediakan lingkungan yang terhubung ke sesi X yang sedang berjalan, dengan akses ke dbusdll. Misalnya, di Ubuntu, nmcli(Network Manager) akan bekerja di dua pendekatan di atas, tetapi masih gagal di cron.

    * * * * * /usr/bin/screen -dm

    Tambahkan baris di atas ke cron, biarkan berjalan sekali, matikan kembali. Hubungkan ke sesi layar Anda (layar -r). Jika Anda memeriksa sesi layar telah dibuat (dengan ps) sadar bahwa mereka kadang-kadang menggunakan huruf besar (misalnya ps | grep SCREEN)

    Sekarang genap nmclidan yang serupa akan gagal.

Kue kering
sumber
22

Anda dapat menjalankan:

env - your_command arguments

Ini akan menjalankan perintah_Anda dengan lingkungan kosong.

dimba
sumber
4
cron tidak berjalan di lingkungan yang benar-benar kosong, bukan?
jldupont
2
gregseth mengidentifikasi variabel yang dimasukkan dalam lingkungan oleh cron. Anda bisa memasukkan variabel itu di baris perintah. $ env - PATH = "$ PATH" command args
DragonFax
4
@DragonFax @dimba Saya menggunakan env - HOME="$HOME" LOGNAME="$USER" PATH="/usr/bin:/bin" SHELL="$(which sh)" command argumentsyang tampaknya melakukan trik
l0b0
Ini adalah metode sederhana yang bagus untuk menguji skrip di lingkungan "tidak bersahabat" atau tidak dikenal. Jika Anda cukup eksplisit sehingga akan berjalan dalam ini, itu akan berjalan di bawah cron.
Oli
13

Menjawab enam tahun kemudian: masalah ketidakcocokan lingkungan adalah salah satu masalah yang diselesaikan oleh systemd"timer" sebagai pengganti cron. Apakah Anda menjalankan systemd "layanan" dari CLI atau melalui cron, ia menerima lingkungan yang persis sama, menghindari masalah ketidakcocokan lingkungan.

Masalah paling umum yang menyebabkan pekerjaan cron gagal ketika mereka lulus secara manual adalah pembatasan standar yang $PATHditetapkan oleh cron, yang ini pada Ubuntu 16.04:

"/usr/bin:/bin"

Sebaliknya, default $PATHset oleh systemdpada Ubuntu 16.04 adalah:

"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

Jadi sudah ada peluang yang lebih baik bahwa timer systemd akan menemukan biner tanpa kerumitan lebih lanjut.

The downside dengan timer systemd, adalah ada sedikit lebih banyak waktu untuk mengaturnya. Anda pertama-tama membuat file "service" untuk menentukan apa yang ingin Anda jalankan dan file "timer" untuk menentukan jadwal untuk menjalankannya dan akhirnya "mengaktifkan" timer untuk mengaktifkannya.

Mark Stosberg
sumber
10

Buat pekerjaan cron yang menjalankan env dan mengarahkan ulang stdout ke file. Gunakan file di samping "env -" untuk menciptakan lingkungan yang sama dengan pekerjaan cron.

Jens Carlberg
sumber
Maaf ini membuatku bingung. apakah "env - script" tidak cukup?
Jorge Vargas
Itu akan memberi Anda lingkungan kosong. Saat Anda menjalankan skrip melalui cron, lingkungan tidak kosong.
Jens Carlberg
3

Jangan lupa bahwa karena cron's parent init, ia menjalankan program tanpa terminal pengendali. Anda dapat mensimulasikannya dengan alat seperti ini:

http://libslack.org/daemon/

Randy Proctor
sumber
2

Secara default, cronjalankan pekerjaannya menggunakan apa pun ide sistem Anda sh. Ini bisa menjadi aktual Bourne shell atau dash, ash, kshatau bash(atau yang lain) symlinked ke sh(dan sebagai hasilnya berjalan dalam mode POSIX).

Hal terbaik untuk dilakukan adalah memastikan skrip Anda memiliki apa yang mereka butuhkan dan menganggap tidak ada yang disediakan untuk mereka. Oleh karena itu, Anda harus menggunakan spesifikasi direktori lengkap dan mengatur variabel lingkungan seperti $PATHAnda.

Dijeda sampai pemberitahuan lebih lanjut.
sumber
Inilah yang saya coba pecahkan. Kami mendapatkan banyak masalah dengan skrip yang menganggap sesuatu karena kesalahan. Melakukan jalur penuh dan mengatur variabel env dan semua sampah berakhir dengan garis cron besar yang tidak dapat dipelihara
Jorge Vargas
re * sh maaf saya tumbuh dengan bash = shell jadi sulit bagi saya untuk mengingat shell alternatif (dan terkadang lebih baik).
Jorge Vargas
1
@ Jorge: garis-garis di crontab seharusnya cukup pendek. Anda harus melakukan semua pengaturan yang Anda butuhkan dalam skrip (atau skrip wrapper). Berikut ini adalah garis khas dari crontab sebagai contoh: 0 0 * * 1 /path/to/executable >/dev/null 2>&1dan kemudian, dalam "executable" saya akan menetapkan nilai untuk $PATH, dll., Dan menggunakan spesifikasi direktori lengkap untuk menginput dan output file, dll. Sebagai contoh:/path/to/do_something /another/path/input_file /another/path/to/output_file
Dijeda sampai pemberitahuan lebih lanjut.
1

Cara sederhana lain yang saya temukan (tetapi mungkin rentan kesalahan, saya masih menguji) adalah untuk mencari file profil pengguna Anda sebelum perintah Anda.

Mengedit skrip /etc/cron.d/:

* * * * * user1 comand-that-needs-env-vars

Akan berubah menjadi:

* * * * * user1 source ~/.bash_profile; source ~/.bashrc; comand-that-needs-env-vars

Kotor, tapi itu menyelesaikan pekerjaan untukku. Apakah ada cara untuk mensimulasikan login? Hanya perintah yang bisa Anda jalankan? bash --logintidak bekerja Sepertinya itu akan menjadi cara yang lebih baik.

EDIT: Ini tampaknya menjadi solusi yang solid: http://www.epicserve.com/blog/2012/feb/7/my-notes-cron-directory-etccrond-ubuntu-1110/

* * * * * root su --session-command="comand-that-needs-env-vars" user1 -l
empat43
sumber
1

Jawaban yang diterima memang memberikan cara untuk menjalankan skrip dengan lingkungan yang akan digunakan cron. Seperti yang ditunjukkan orang lain, ini bukan satu-satunya kriteria yang dibutuhkan untuk debugging pekerjaan cron.

Memang, cron juga menggunakan terminal non-interaktif, tanpa input yang terpasang, dll.

Jika itu membantu, saya telah menulis skrip yang memungkinkan tanpa kesulitan menjalankan perintah / skrip karena akan dijalankan oleh cron. Meminta dengan perintah / skrip Anda sebagai argumen pertama dan Anda baik.

Skrip ini juga dihosting (dan mungkin diperbarui) di Github .

#!/bin/bash
# Run as if it was called from cron, that is to say:
#  * with a modified environment
#  * with a specific shell, which may or may not be bash
#  * without an attached input terminal
#  * in a non-interactive shell

function usage(){
    echo "$0 - Run a script or a command as it would be in a cron job, then display its output"
    echo "Usage:"
    echo "   $0 [command | script]"
}

if [ "$1" == "-h" -o "$1" == "--help" ]; then
    usage
    exit 0
fi

if [ $(whoami) != "root" ]; then
    echo "Only root is supported at the moment"
    exit 1
fi

# This file should contain the cron environment.
cron_env="/root/cron-env"
if [ ! -f "$cron_env" ]; then
    echo "Unable to find $cron_env"
    echo "To generate it, run \"/usr/bin/env > /root/cron-env\" as a cron job"
    exit 0
fi

# It will be a nightmare to expand "$@" inside a shell -c argument.
# Let's rather generate a string where we manually expand-and-quote the arguments
env_string="/usr/bin/env -i "
for envi in $(cat "$cron_env"); do
   env_string="${env_string} $envi "
done

cmd_string=""
for arg in "$@"; do
    cmd_string="${cmd_string} \"${arg}\" "
done

# Which shell should we use?
the_shell=$(grep -E "^SHELL=" /root/cron-env | sed 's/SHELL=//')
echo "Running with $the_shell the following command: $cmd_string"


# Let's route the output in a file
# and do not provide any input (so that the command is executed without an attached terminal)
so=$(mktemp "/tmp/fakecron.out.XXXX")
se=$(mktemp "/tmp/fakecron.err.XXXX")
"$the_shell" -c "$env_string $cmd_string" >"$so" 2>"$se" < /dev/null

echo -e "Done. Here is \033[1mstdout\033[0m:"
cat "$so"
echo -e "Done. Here is \033[1mstderr\033[0m:"
cat "$se"
rm "$so" "$se"
Daladim
sumber
0

Jawaban https://stackoverflow.com/a/2546509/5593430 menunjukkan cara mendapatkan lingkungan cron dan menggunakannya untuk skrip Anda. Namun ketahuilah bahwa lingkungan dapat berbeda tergantung pada file crontab yang Anda gunakan. Saya membuat tiga entri cron berbeda untuk menyelamatkan lingkungan melalui env > log. Ini adalah hasil di Amazon Linux 4.4.35-33.55.amzn1.x86_64.

1. Global / etc / crontab dengan pengguna root

MAILTO=root
SHELL=/bin/bash
USER=root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
PWD=/
LANG=en_US.UTF-8
SHLVL=1
HOME=/
LOGNAME=root
_=/bin/env

2. crontab pengguna root ( crontab -e)

SHELL=/bin/sh
USER=root
PATH=/usr/bin:/bin
PWD=/root
LANG=en_US.UTF-8
SHLVL=1
HOME=/root
LOGNAME=root
_=/usr/bin/env

3. Script di /etc/cron.hourly/

MAILTO=root
SHELL=/bin/bash
USER=root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
_=/bin/env
PWD=/
LANG=en_US.UTF-8
SHLVL=3
HOME=/
LOGNAME=root

Yang terpenting PATH, PWDdan HOMEberbeda. Pastikan untuk mengatur ini dalam skrip cron Anda untuk bergantung pada lingkungan yang stabil.

Maikel
sumber
-8

Saya tidak percaya ada; satu-satunya cara saya tahu untuk menguji pekerjaan cron adalah mengaturnya untuk menjalankan satu atau dua menit di masa depan dan kemudian menunggu.

jn80842
sumber
Inilah sebabnya saya meminta simulasi
Jorge Vargas