Kiểm tra xem tập lệnh có được khởi động bằng cron không, thay vì được gọi thủ công


23

Có bất kỳ biến nào mà cron đặt khi chạy chương trình không? Nếu kịch bản được chạy bởi cron, tôi muốn bỏ qua một số phần; mặt khác gọi những phần đó

Làm thế nào tôi có thể biết nếu tập lệnh Bash được bắt đầu bằng cron?


Tại sao bạn không chỉ là chúng tôi ps?
terdon


@terdon: có lẽ vì psđược ghi lại khá tệ (đặc biệt là phiên bản của Linux hỗ trợ một số kiểu cú pháp khác nhau) và trang man thậm chí còn dày đặc và khó hiểu hơn hầu hết các công cụ. Tôi nghi ngờ hầu hết mọi người thậm chí không nhận ra một công cụ hữu ích và linh hoạt như thế nào ps.
cas

Câu trả lời:


31

Tôi không biết rằng cronbất cứ điều gì đối với môi trường của nó theo mặc định có thể được sử dụng ở đây, nhưng có một vài điều bạn có thể làm để có được hiệu quả mong muốn.

1) Tạo một liên kết cứng hoặc mềm đến tệp tập lệnh, ví dụ như vậy, myscriptmyscript_via_crontrỏ đến cùng một tệp. Sau đó, bạn có thể kiểm tra giá trị $0bên trong tập lệnh khi bạn muốn chạy có điều kiện hoặc bỏ qua một số phần nhất định của mã. Đặt tên thích hợp trong crontab của bạn và bạn đã đặt.

2) Thêm tùy chọn vào tập lệnh và đặt tùy chọn đó trong lệnh gọi crontab. Ví dụ: thêm một tùy chọn -c, cho biết kịch bản chạy hoặc bỏ qua các phần thích hợp của mã và thêm -cvào tên lệnh trong crontab của bạn.

Và tất nhiên, cron có thể đặt các biến môi trường tùy ý, vì vậy bạn chỉ cần đặt một dòng như RUN_BY_CRON="TRUE"trong crontab của bạn và kiểm tra giá trị của nó trong tập lệnh của bạn.


7
+1 cho RUN_BY_CRON = true
cas

câu trả lời của cas đang hoạt động rất tốt và có thể được sử dụng cho bất cứ điều gì khác nữa
Deian

19

Các kịch bản chạy từ cron không được chạy trong các vỏ tương tác. Không phải là kịch bản khởi động. Sự khác biệt là các shell tương tác có STDIN và STDOUT được gắn vào một tty.

Cách 1: kiểm tra xem có $-bao gồm icờ không. iđược thiết lập cho các vỏ tương tác.

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

Cách 2: kiểm tra $PS1là trống.

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

tham khảo: http://techdoc.kvindesland.no/linux/gnubooks/bash/bashref_54.html

Phương pháp 3: kiểm tra tty của bạn. nó không phải đáng tin cậy, nhưng đối với công việc cron đơn giản bạn nên có ok, như cron không theo mặc định phân bổ một tty đến một kịch bản.

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

Tuy nhiên, hãy nhớ rằng bạn có thể sử dụng trình bao tương tác bằng cách sử dụng -i, nhưng có lẽ bạn sẽ biết nếu bạn đang làm điều này ...


1
Lưu ý rằng lệnh $ PS1 không hoạt động khi kiểm tra xem tập lệnh có được khởi động bởi systemd hay không. $ - one does
mveroone

1
Liên kết Đại học Winnipeg của bạn bị hỏng.
WinEunuuchs2Unix

1
@TimKennedy Bạn được chào đón .... từ Edmonton :)
WinEunuuchs2Unix

'case "$ -" in' dường như không hoạt động trong các tập lệnh bash.
Hobadee

@Hobadee - mỗi bashtôi có quyền truy cập đều có $ -, cũng như dashksh. ngay cả các vỏ bị hạn chế trong Solaris cũng có nó. Nền tảng nào bạn đang cố gắng sử dụng nó khi nó không hoạt động? Điều gì case "$-" in *i*) echo true ;; *) echo false ;; esaccho bạn thấy?
Tim Kennedy

7

Trước tiên, hãy lấy PID của cron, sau đó lấy PID gốc của quy trình hiện tại (PPID) và so sánh chúng:

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

Nếu tập lệnh của bạn được bắt đầu bởi một quy trình khác có thể đã được bắt đầu bằng cron, thì bạn có thể đi bộ trở lại các bộ cha mẹ cho đến khi bạn nhận được $ CRONPID hoặc 1 (PID của init).

một cái gì đó như thế này, có lẽ (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

Từ Deian: Đây là phiên bản được thử nghiệm trên 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}"

1
Trên Solaris cron bắt đầu một shell và shell chạy script, chính nó bắt đầu một shell khác. Vì vậy, pid cha trong kịch bản không phải là pid của cron.
ceving

4

Nếu tệp tập lệnh của bạn được gọi bởi cronvà nó chứa một vỏ trong dòng đầu tiên như #!/bin/bashbạn cần tìm tên cha-mẹ cho mục đích của bạn.

1) cronđược gọi vào thời điểm đã cho trong bạn crontab, thực thi shell 2) shell thực thi tập lệnh của bạn 3) tập lệnh của bạn đang chạy

PID cha có sẵn trong bash dưới dạng biến $PPID . Các pslệnh để có được PID mẹ của phụ huynh PID là:

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

nhưng chúng ta cần tên của lệnh chứ không phải pid, vì vậy chúng ta gọi

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

bây giờ chúng ta chỉ cần kiểm tra kết quả cho "cron"

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

Bây giờ bạn có thể kiểm tra bất cứ nơi nào trong kịch bản của bạn

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

Chúc may mắn!


Điều này chỉ hoạt động cho Linux ps. Đối với MacOS (cũng như Linux, có thể * BSD cũng vậy), bạn có thể sử dụng P_COMMAND sau:P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
mdd

1

Hoạt động trên FreeBSD hoặc trên 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

Bạn có thể đi xa đến cây quy trình như bạn muốn.


1

Một giải pháp chung cho câu hỏi "là đầu ra của tôi là thiết bị đầu cuối hay tôi đang chạy từ tập lệnh" là:

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

0

Một đơn giản echo $TERM | mail me@domain.comtrong cron đã cho tôi thấy rằng trên cả Linux và AIX, cron dường như được đặt $TERMthành 'câm'.

Bây giờ về mặt lý thuyết có thể vẫn còn những thiết bị đầu cuối câm thực sự xung quanh, nhưng tôi nghi ngờ rằng trong hầu hết các trường hợp, điều đó sẽ đủ ...


0

Không có câu trả lời xác thực, nhưng các biến prompt ( $PS1) và terminal ( $TERM) khá tốt ở đây. Một số hệ thống được đặt TERM=dumbtrong khi hầu hết để trống, vì vậy chúng tôi sẽ kiểm tra xem:

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

Đoạn mã trên thay thế từ "câm" khi không có giá trị cho $TERM. Do đó, các điều kiện kích hoạt khi không có $TERMhoặc $TERMđược đặt thành "câm" hoặc nếu $PS1biến không trống.

Tôi đã thử nghiệm điều này trên Debian 9 ( TERM=), CentOS 6.4 & 7.4 ( TERM=dumb) và FreeBSD 7.3 ( TERM=).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.