Periksa apakah skrip dimulai dengan cron, daripada dipanggil secara manual

23

Apakah ada variabel yang diset cron saat menjalankan program? Jika skrip dijalankan oleh cron, saya ingin melewati beberapa bagian; jika tidak panggil bagian-bagian itu.

Bagaimana saya bisa tahu jika skrip Bash dimulai oleh cron?

bunga aster
sumber
Mengapa kamu tidak hanya kami saja ps?
terdon
@terdon: mungkin karena psdidokumentasikan dengan sangat buruk (terutama versi Linux yang mendukung beberapa gaya sintaks yang berbeda) dan halaman manual bahkan lebih padat dan samar daripada kebanyakan alat. Saya curiga kebanyakan orang bahkan tidak menyadari betapa berguna dan serbagunanya sebuah alat ps.
cas

Jawaban:

31

Saya tidak mengetahui bahwa cronmelakukan apa saja pada lingkungannya secara default yang dapat digunakan di sini, tetapi ada beberapa hal yang dapat Anda lakukan untuk mendapatkan efek yang diinginkan.

1) Buat tautan keras atau lunak ke file skrip, sehingga, misalnya, myscriptdan myscript_via_cronarahkan ke file yang sama. Anda kemudian dapat menguji nilai $0di dalam skrip ketika Anda ingin menjalankan atau menghilangkan bagian-bagian tertentu dari kode. Masukkan nama yang sesuai di crontab Anda, dan Anda siap.

2) Tambahkan opsi ke skrip, dan atur opsi itu dalam permintaan crontab. Misalnya, tambahkan opsi -c, yang memberi tahu skrip untuk menjalankan atau menghilangkan bagian kode yang sesuai, dan menambahkan -cnama perintah di crontab Anda.

Dan tentu saja, cron dapat mengatur variabel lingkungan yang berubah-ubah, sehingga Anda bisa meletakkan baris seperti RUN_BY_CRON="TRUE"di crontab Anda, dan memeriksa nilainya di skrip Anda.

D_Bye
sumber
7
+1 untuk RUN_BY_CRON = true
cas
jawaban oleh cas bekerja sangat baik dan dapat digunakan untuk hal lain juga
Deian
19

Skrip yang dijalankan dari cron tidak dijalankan di shell interaktif. Skrip startup juga tidak. Perbedaannya adalah bahwa kerang interaktif memiliki STDIN dan STDOUT yang melekat pada tty.

Metode 1: periksa apakah $-menyertakan ibendera. idiatur untuk shell interaktif.

case "$-" in
    *i*)
        interactive=1
        ;;
    *)
        not_interactive=1
        ;;
esac

Metode 2: cek $PS1kosong

if [ -z "$PS1" ]; then
    not_interactive=1 
else
    interactive=1
fi

referensi: http://techdoc.kvindesland.no/linux/gnubooks/bash/bashref_54.html

Metode 3: uji tty Anda. itu tidak seperti yang diandalkan, tapi untuk pekerjaan cron sederhana Anda harus ok, seperti cron tidak secara default mengalokasikan tty untuk script.

if [ -t 0 ]; then
    interactive=1
else
    non_interactive=1
fi

Ingatlah bahwa Anda dapat memaksa shell interaktif menggunakan -i, tetapi Anda mungkin akan sadar jika Anda melakukan ini ...

Tim Kennedy
sumber
1
Perhatikan bahwa perintah $ PS1 tidak berfungsi ketika memeriksa apakah skrip dijalankan oleh systemd atau tidak. $ - satu tidak
mveroone
1
Tautan Universitas Winnipeg Anda rusak.
WinEunuuchs2Unix
1
@TimKennedy Terima kasih .... dari Edmonton :)
WinEunuuchs2Unix
'case "$ -" in' tampaknya tidak berfungsi dalam skrip bash.
Hobadee
@Hobadee - setiap bashsaya memiliki akses ke memiliki $ -, seperti halnya dashdan ksh. bahkan cangkang terbatas di Solaris memilikinya. Platform apa yang Anda coba gunakan di tempat yang tidak berfungsi? Apa yang case "$-" in *i*) echo true ;; *) echo false ;; esacditunjukkan kepada Anda?
Tim Kennedy
7

Pertama, dapatkan PID cron, lalu dapatkan PID (PPID) induk proses saat ini, dan bandingkan:

CRONPID=$(ps ho %p -C cron)
PPID=$(ps ho %P -p $$)
if [ $CRONPID -eq $PPID ] ; then echo Cron is our parent. ; fi

Jika skrip Anda dimulai oleh proses lain yang mungkin dimulai oleh cron, maka Anda dapat berjalan mundur dari PID induk hingga Anda mendapatkan $ CRONPID atau 1 (PID init).

sesuatu seperti ini, mungkin (Untested-But-It-Might-Work <TM>):

PPID=$$   # start from current PID
CRON_IS_PARENT=0
CRONPID=$(ps ho %p -C cron)
while [ $CRON_IS_PARENT -ne 1 ] && [ $PPID -ne 1 ] ; do
  PPID=$(ps ho %P -p $PPID)
  [ $CRONPID -eq $PPID ] && CRON_IS_PARENT=1
done

Dari Deian: Ini adalah versi yang diuji pada RedHat Linux

# start from current PID
MYPID=$$
CRON_IS_PARENT=0
# this might return a list of multiple PIDs
CRONPIDS=$(ps ho %p -C crond)

CPID=$MYPID
while [ $CRON_IS_PARENT -ne 1 ] && [ $CPID -ne 1 ] ; do
        CPID_STR=$(ps ho %P -p $CPID)
        # the ParentPID came up as a string with leading spaces
        # this will convert it to int
        CPID=$(($CPID_STR))
        # now loop the CRON PIDs and compare them with the CPID
        for CRONPID in $CRONPIDS ; do
                [ $CRONPID -eq $CPID ] && CRON_IS_PARENT=1
                # we could leave earlier but it's okay like that too
        done
done

# now do whatever you want with the information
if [ "$CRON_IS_PARENT" == "1" ]; then
        CRON_CALL="Y"
else
        CRON_CALL="N"
fi

echo "CRON Call: ${CRON_CALL}"
cas
sumber
1
Pada Solaris cron memulai sebuah shell dan shell menjalankan skrip, yang dengan sendirinya memulai shell lain. Jadi pid orang tua dalam skrip bukanlah pid dari cron.
ceving
4

Jika file skrip Anda dipanggil oleh crondan berisi shell di baris pertama seperti #!/bin/bashAnda harus mencari nama induk-induk untuk tujuan Anda.

1) crondipanggil pada waktu yang ditentukan di Anda crontab, menjalankan shell 2) shell mengeksekusi skrip Anda 3) skrip Anda sedang berjalan

PID induk tersedia dalam bash sebagai variabel $PPID. The psperintah untuk mendapatkan PID induk dari induk PID adalah:

PPPID=`ps h -o ppid= $PPID`

tetapi kita membutuhkan nama perintah, bukan pid, jadi kita panggil

P_COMMAND=`ps h -o %c $PPPID`

sekarang kita hanya perlu menguji hasilnya untuk "cron"

if [ "$P_COMMAND" == "cron" ]; then
  RUNNING_FROM_CRON=1
fi

Sekarang Anda dapat menguji di mana saja dalam skrip Anda

if [ "$RUNNING_FROM_CRON" == "1" ]; then
  ## do something when running from cron
else
  ## do something when running from shell
fi

Semoga berhasil!

Olray
sumber
Ini hanya berfungsi untuk Linux ps. Untuk MacOS (dan juga Linux, mungkin * BSD juga), Anda dapat menggunakan P_COMMAND berikut:P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
mdd
1

Bekerja di FreeBSD atau di Linux:

if [ "Z$(ps o comm="" -p $(ps o ppid="" -p $$))" == "Zcron" -o \
     "Z$(ps o comm="" -p $(ps o ppid="" -p $(ps o ppid="" -p $$)))" == "Zcron" ]
then
    echo "Called from cron"
else
    echo "Not called from cron"
fi

Anda dapat naik sejauh pohon proses yang Anda inginkan.

Ted Rypma
sumber
1

Solusi umum untuk pertanyaan "adalah keluaran saya terminal atau saya menjalankan skrip" adalah:

( : > /dev/tty) && dev_tty_good=y || dev_tty_good=n
Stephen
sumber
0

Sederhana echo $TERM | mail [email protected]di cron menunjukkan kepada saya bahwa pada Linux dan AIX, cron tampaknya diatur $TERMke 'bodoh'.

Sekarang secara teoritis mungkin masih ada terminal bisu yang sebenarnya di sekitar, tapi saya menduga bahwa untuk sebagian besar kesempatan, itu sudah cukup ...

vegivamp
sumber
0

Tidak ada jawaban otoritatif, tetapi variabel prompt ( $PS1) dan terminal ( $TERM) cukup baik di sini. Beberapa sistem mengatur TERM=dumbsementara sebagian besar membiarkannya kosong, jadi kami hanya akan memeriksa:

if [ "${TERM:-dumb}$PS1" != "dumb" ]; then
  echo "This is not a cron job"
fi

Kode di atas menggantikan kata "bodoh" ketika tidak ada nilai untuk $TERM. Oleh karena itu, kondisional akan menyala ketika tidak ada $TERMatau $TERMdiatur ke "bodoh" atau jika $PS1variabel tidak kosong.

Saya sudah menguji ini pada Debian 9 ( TERM=), CentOS 6.4 & 7.4 ( TERM=dumb), dan FreeBSD 7.3 ( TERM=).

Adam Katz
sumber