ตรวจสอบว่าสคริปต์เริ่มโดย cron แทนที่จะเรียกใช้ด้วยตนเอง


23

มีตัวแปรใดที่ cron ตั้งไว้เมื่อรันโปรแกรมหรือไม่? หากสคริปต์ทำงานโดย cron ฉันต้องการข้ามบางส่วน มิฉะนั้นจะเรียกใช้ส่วนเหล่านั้น

ฉันจะทราบได้อย่างไรว่าสคริปต์ Bash เริ่มต้นโดย cron


ทำไมคุณไม่เพียงแค่เราps?
terdon


@terdon: อาจเป็นเพราะpsเอกสารไม่ดีพอสมควร (โดยเฉพาะเวอร์ชันของ Linux ที่สนับสนุนรูปแบบไวยากรณ์ที่แตกต่างกัน) และ man page นั้นมีความหนาแน่นและเป็นความลับมากกว่าเครื่องมือส่วนใหญ่ ฉันสงสัยว่าคนส่วนใหญ่ไม่ได้ตระหนักถึงเพียงวิธีที่มีประโยชน์และหลากหลายเครื่องมือที่psสามารถ
cas

คำตอบ:


31

ฉันไม่ทราบว่าcronจะทำอะไรกับสภาพแวดล้อมโดยค่าเริ่มต้นที่สามารถใช้งานได้ที่นี่ แต่มีสองสิ่งที่คุณสามารถทำได้เพื่อให้ได้ผลตามที่ต้องการ

1) สร้างฮาร์ดลิงก์หรือซอฟต์ลิงก์ไปยังไฟล์สคริปต์เพื่อให้ตัวอย่างmyscriptและmyscript_via_cronชี้ไปที่ไฟล์เดียวกัน จากนั้นคุณสามารถทดสอบค่าของ$0สคริปต์ภายในเมื่อคุณต้องการเรียกใช้ตามเงื่อนไขหรือละเว้นบางส่วนของรหัส ใส่ชื่อที่เหมาะสมใน crontab ของคุณและคุณตั้ง

2) เพิ่มตัวเลือกให้กับสคริปต์และตั้งค่าตัวเลือกนั้นในการเรียกใช้ crontab ตัวอย่างเช่นเพิ่มตัวเลือก-cที่บอกให้สคริปต์เรียกใช้หรือละเว้นส่วนที่เหมาะสมของรหัสและเพิ่ม-cชื่อคำสั่งใน crontab ของคุณ

และแน่นอน cron สามารถตั้งค่าตัวแปรสภาพแวดล้อมตามอำเภอใจดังนั้นคุณสามารถใส่บรรทัดRUN_BY_CRON="TRUE"ใน crontab ของคุณและตรวจสอบค่าในสคริปต์ของคุณ


7
+1 สำหรับ RUN_BY_CRON = true
CAS

คำตอบโดย cas ทำงานได้ดีมากและสามารถนำไปใช้กับสิ่งอื่นได้เช่นกัน
Deian

19

สคริปต์ที่เรียกใช้จาก cron ไม่ได้ทำงานในเชลล์แบบโต้ตอบ ไม่มีสคริปต์เริ่มต้น ความแตกต่างคือเชลล์แบบโต้ตอบมี STDIN และ STDOUT แนบกับ tty

วิธีที่ 1: ตรวจสอบว่า$-มีการiตั้งค่าสถานะหรือไม่ iถูกตั้งค่าสำหรับเชลล์แบบโต้ตอบ

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

วิธีที่ 2: ตรวจสอบ$PS1ว่างเปล่า

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

การอ้างอิง: http://techdoc.kvindesland.no/linux/gnubooks/bash/bashref_54.html

วิธีที่ 3: ทดสอบ tty ของคุณ มันไม่น่าเชื่อถือเท่าไหร่ แต่สำหรับงาน cron แบบธรรมดาคุณควรจะใช้ได้เนื่องจาก cron ไม่ได้จัดสรร tty ให้กับสคริปต์ตามค่าเริ่มต้น

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

โปรดทราบว่าคุณสามารถบังคับใช้เชลล์แบบโต้ตอบ-iได้ แต่คุณอาจต้องระวังหากคุณทำเช่นนี้ ...


1
โปรดทราบว่าคำสั่ง $ PS1 ไม่ทำงานเมื่อตรวจสอบว่าสคริปต์เริ่มโดย systemd หรือไม่ สิ่งที่ $ $ ทำ
mveroone

1
ลิงก์ Winnipeg University ของคุณใช้งานไม่ได้
WinEunuuchs2Unix

1
@TimKennedy ยินดีต้อนรับคุณ .... จาก Edmonton :)
WinEunuuchs2Unix

'case "$ -" in' ไม่ทำงานในสคริปต์ทุบตี
Hobadee

@Hobadee - ทุกbashฉันมีการเข้าถึงมี $ - เช่นเดียวกับและdash kshแม้แต่กระสุนที่ จำกัด ใน Solaris ก็มีเช่นกัน คุณกำลังพยายามใช้แพลตฟอร์มใดในที่ที่ไม่ทำงาน สิ่งที่ไม่case "$-" in *i*) echo true ;; *) echo false ;; esacแสดง?
Tim Kennedy

7

ก่อนอื่นรับ PID ของ cron จากนั้นรับ parent PID ของกระบวนการปัจจุบัน (PPID) และเปรียบเทียบกับ:

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

หากสคริปต์ของคุณเริ่มต้นด้วยกระบวนการอื่นที่อาจเริ่มต้นโดย cron คุณสามารถเดินสำรอง PID หลักจนกว่าคุณจะได้รับ $ CRONPID หรือ 1 (PID ของ init)

บางสิ่งเช่นนี้อาจจะ (ยังไม่ทดลอง - แต่ - อาจ - ทำงาน - <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

จาก Deian: นี่เป็นรุ่นทดสอบบน 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
บน Solaris cron เริ่มเชลล์และเชลล์เรียกใช้สคริปต์ซึ่งตัวเองเริ่มต้นเชลล์อื่น ดังนั้น parent pid ในสคริปต์จึงไม่ใช่ pid ของ cron
ceving

4

หากไฟล์สคริปต์ของคุณถูกเรียกใช้cronและมีเชลล์ในบรรทัดแรกเช่น#!/bin/bashคุณจำเป็นต้องค้นหาชื่อพาเรนต์พาเรนต์เพื่อวัตถุประสงค์ของคุณ

1) cronถูกเรียกใช้ในเวลาที่กำหนดในcrontabการดำเนินการเชลล์ 2) เชลล์ดำเนินการสคริปต์ของคุณ 3) สคริปต์ของคุณกำลังทำงาน

ผู้ปกครอง PID $PPIDมีให้บริการในทุบตีเป็นตัวแปร psคำสั่งที่จะได้รับ PID แม่ของแม่ PID คือ:

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

แต่เราต้องการชื่อของคำสั่งไม่ใช่ pid ดังนั้นเราจึงเรียก

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

ตอนนี้เราแค่ต้องทดสอบผลลัพธ์สำหรับ "cron"

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

ตอนนี้คุณสามารถทดสอบที่ใดก็ได้ในสคริปต์ของคุณ

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

โชคดี!


ใช้งานได้กับ Linux ps เท่านั้น สำหรับ MacOS (เช่นเดียวกับลินุกซ์อาจจะ * BSD เกินไป), คุณสามารถใช้ P_COMMAND ต่อไปนี้:P_COMMAND=$(basename -a $(ps h -o comm $PPPID))
MDD

1

ใช้งานได้กับ FreeBSD หรือบน 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

คุณสามารถไปไกลถึงต้นไม้กระบวนการตามที่คุณต้องการ


1

โซลูชันทั่วไปสำหรับคำถาม "คือผลลัพธ์ของฉันเทอร์มินัลหรือฉันกำลังเรียกใช้จากสคริปต์" คือ:

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

0

echo $TERM | mail me@domain.comcron ง่าย ๆแสดงให้ฉันเห็นว่าทั้ง Linux และ AIX ดูเหมือนว่า cron จะตั้งค่า$TERMเป็น 'dumb'

ทฤษฏีตอนนี้อาจจะยังมีเทอร์มินัลใบ้อยู่รอบ ๆ แต่ฉันสงสัยว่าสำหรับโอกาสส่วนใหญ่มันน่าจะพอเพียง ...


0

ไม่มีคำตอบที่เชื่อถือได้ แต่ตัวแปรprompt ( $PS1) และ terminal ( $TERM) นั้นค่อนข้างดีในที่นี้ บางระบบตั้งค่าไว้TERM=dumbส่วนใหญ่จะปล่อยให้ว่างเปล่าดังนั้นเราจะตรวจสอบอย่างใดอย่างหนึ่ง:

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

รหัสข้างต้นทดแทนคำว่า "โง่" $TERMเมื่อมีความคุ้มค่าไม่มี ดังนั้นเงื่อนไขการยิงเมื่อไม่มี$TERMหรือ$TERMถูกตั้งค่าเป็น "dumb" หรือหาก$PS1ตัวแปรไม่ว่างเปล่า

ฉันได้ทดสอบสิ่งนี้กับ Debian 9 ( TERM=), CentOS 6.4 & 7.4 ( TERM=dumb), และ FreeBSD 7.3 ( TERM=)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.