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?


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.


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.


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.


22

Anda dapat menjalankan:

env - your_command arguments

Ini akan menjalankan perintah_Anda dengan lingkungan kosong.


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.


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.


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


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.


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

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"

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.


-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.


Inilah sebabnya saya meminta simulasi
Jorge Vargas
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.