web-dev-qa-db-ja.com

スクリプトが手動で呼び出されるのではなく、cronによって開始されているかどうかを確認します

プログラムの実行時にcronが設定する変数はありますか?スクリプトがcronで実行されている場合は、一部をスキップします。それ以外の場合は、それらの部分を呼び出します。

Bashスクリプトがcronによって起動されているかどうかを確認するにはどうすればよいですか?

23
daisy

ここではcronがデフォルトで環境に役立つことを認識していませんが、目的の効果を得るためにできることがいくつかあります。

1)スクリプトファイルへのハードリンクまたはソフトリンクを作成して、たとえば、myscriptmyscript_via_cronが同じファイルを指すようにします。次に、条件付きでコードを実行したり、コードの特定の部分を省略したりするときに、スクリプト内で$0の値をテストできます。適切な名前をcrontabに入力すれば、設定は完了です。

2)スクリプトにオプションを追加し、crontab呼び出しでそのオプションを設定します。たとえば、スクリプトにコードの適切な部分を実行または省略するように指示するオプション-cを追加し、crontabのコマンド名に-cを追加します。

そしてもちろん、cron canは任意の環境変数を設定するので、RUN_BY_CRON="TRUE"のような行をcrontabに入れて、スクリプトでその値を確認できます。

32
D_Bye

Cronから実行されるスクリプトは、対話型シェルでは実行されません。どちらも起動スクリプトではありません。違いは、対話型シェルではttyにSTDINとSTDOUTが接続されていることです。

方法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をテストします。 asは信頼できませんが、cronはデフォルトでスクリプトにttyを割り当てないため、単純なcronジョブの場合は問題ありません。

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

ただし、-iを使用してインタラクティブシェルを強制することはできますが、これを実行しているかどうかはおそらく気になるでしょう...

20
Tim Kennedy

まず、cronのPIDを取得し、次に現在のプロセスの親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によって開始された可能性のある別のプロセスによって開始された場合は、$ CRONPIDまたは1(初期のPID)のいずれかに到達するまで、親PIDに戻ることができます。

このようなもの、多分(未テスト-しかし-それは-仕事-<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}"
8
cas

スクリプトファイルがcronによって呼び出され、そのファイルの最初の行に#!/bin/bashのようなシェルが含まれている場合は、目的に応じて親と親の名前を見つける必要があります。

1)croncrontabで指定された時間に呼び出され、シェルを実行する2)シェルがスクリプトを実行する3)スクリプトが実行されている

親PIDは、変数$PPIDとしてbashで使用できます。親PIDの親PIDを取得するpsコマンドは次のとおりです。

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

幸運を!

5
Olray

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
Ted Rypma

「私の出力はターミナルですか、それともスクリプトから実行していますか」という質問に対する一般的な解決策は次のとおりです。

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

誰も言及していない問題があります。 sshを使用してリモートコンピューターからスクリプトを実行すると、ほとんどのテストが失敗します。

ssh [email protected] ./SmartHome.sh

次に(たとえば)[-t 0]および(:>/dev/tty)と$ TERMはすべて間違っていることを示します(OK、実際にはすべて正しいですが、必要なものを示していません)。

したがって、最も単純なユニバーサルソリューションは、2で最も支持されている回答で述べたように、引数を指定してcronからスクリプトを呼び出すことです。その後、他の呼び出しはすべて手動実行と見なされます。私は-(ただし、何でもかまいません)をcron呼び出しの最初の引数として使用し、スクリプトで確認します。

[ "$1" == "-" ] && shift || Run_Manually=1
0
Pila

シンプルな echo $TERM | mail [email protected] cronでLinuxとAIXの両方でcronが$TERMを「ダム」に。

今理論的にはまだ実際のダム端末が残っているかもしれませんが、ほとんどの場合、それで十分でしょう...

0
vegivamp

信頼できる回答はありませんが、プロンプト($PS1)とターミナル($TERM)の変数は、シェルの「インタラクティブ」オプションフラグ( 特別なパラメーター と同様、かなりまともです。 $-にはiが含まれています)。一部のシステムはTERM=dumbを設定しますが、ほとんどの場合は空のままにします。そのため、どちらかを確認し、インタラクティブオプションフラグも確認します。

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

上記のコードは、$TERMに値がない場合、Wordの「ダム」を置き換えます。したがって、$TERMまたは$TERMが「ダム」に設定されていない場合、または$PS1変数が空でない場合、または$-がそれ自体と一致しない場合に、条件が発生します。最初のiまでのすべての文字を削除します(iがない場合は何も削除されません)。


または、端末名を確認することもできます。通常、cronは(これを確認してください!)端末を割り当てません。そのため、 ttyPOSIX標準マンデート を実行できます(実際にない場合)端末)出力でnot a ttyと言う必要があります。

if [ "$(tty)" != "not a tty" ]; then
  echo "This is not a cron job"
fi

端末を割り当てないcronを利用する別の方法は、標準入力が開いているかどうかをテストすることです。これは! [ -t 0 ]を使用して行うことができますが、データをスクリプトにパイプ処理している場合、これは不適切な回答になります。


Debian 9と11(TERM=、ttyはnot a tty)、CentOS 6.4&7.4(TERM=dumb、ttyはnot a tty)、およびFreeBSD 7.3で両方のオプションをテストしました&& 11.2(TERM=、ttyはnot a tty)。

0
Adam Katz