Bir cron işini manuel olarak ve hemen çalıştırma


108

(Zaten okudum Yeni bir cron betiğini nasıl test edebilirim? )

Belirli bir sorunum var (cron işi düzgün çalışmıyor veya düzgün çalışmıyor), ancak sorun genel: Cronned komut dosyalarında hata ayıklamak istiyorum. * * * * * Crontab hattı kurabileceğimin farkındayım, ancak bu tamamen tatmin edici bir çözüm değil. Bir cron işini komut satırından, cron çalışıyormuş gibi (aynı kullanıcı, aynı ortam değişkenleri, vb.) Çalıştırabilmek isterim. Bunu yapmanın bir yolu var mı? Komut dosyası değişikliklerini test etmek için 60 saniye beklemek zor değildir.


(üzgünüm yorum ekleyemiyorum) 0 30 16 20 *? Böyle bir iş çalıştırmak bile *, tüm fikir işi günlüğüne yazar sürece yanlış neler olduğunu görmek için komut çıkışını sağlamaktır, bu quuiet faydasızdır

Yanıtlar:


80

İşte ne yaptım ve bu durumda işe yarıyor gibi görünüyor. En azından, bana bir hata gösteriyor, oysa kullanıcı hatayı göstermediğinden komut satırından çalıştırılıyor.


Adım 1 : Bu satırı geçici olarak kullanıcının crontab'ına koydum:

* * * * *   /usr/bin/env > /home/username/tmp/cron-env

daha sonra dosya yazıldıktan sonra çıkardı.

Adım 2 : Kendimi içeren bir cron ash bash betiği yaptım:

#!/bin/bash
/usr/bin/env -i $(cat /home/username/tmp/cron-env) "$@"

Böylece, söz konusu kullanıcı olarak yapabildim.

run-as-cron /the/problematic/script --with arguments --and parameters

Bu çözüm açıkça sudo veya daha fazla esneklik için bu şekilde yararlanmak için genişletilebilir.

Umarım bu başkalarına yardımcı olur.


8
Bu benim için işe yaramadı ve ben kimseyi yenen için yaptığını merak ediyorum. 1) Neden bash kullanıyorsunuz? Burada gerekli değildir ve içinde bulunmayabilir /usr/bin. 2) Çalışmayan cat …/cron-envbirden fazla satır çıktılar. Sadece /usr/bin/env -i $(cat cron-env) echo $PATHterminalde çalıştırmaya çalışın , ortamı kullanmak yerine tam anlamıyla çıktı verir. 3) Mevcut ortam, taklit edilen cron ortamına sızıyor. Deneyin: export foo=leaked; run-as-cron echo $foo.
Marco

@Marco, bastan çalışır, bu, kullandığım sh'den daha iyi tanımlanmış bir ortamdır. Her şeyi pdksh, ksh (çoklu sürümler), bash ve dash'den kullanırım, bu nedenle, dillerin ortak alt kümesinde çok katı kalırken bile, sh'in "pure" uygulamaları arasındaki farkların farkındayım. :-)
Max Murphy,

7
@Marco 2. catkabuk değiştirme işlemi onları kontrol edebileceğiniz tek bir satıra daralttığı için çalışan çoklu satırlar üretir echo $(cat cron-env ) | wc; Örnek komutunuz, çağıran kabuğun /usr/bin/env -i $(cat cron-env) echo $PATHyerine $PATHkullanılır; bunun yerine, alt ortamdaki yerine geçmek için bir alt kabuk çağırmalıdır, örn /usr/bin/env -i $(cat cron-env) /bin/sh -c 'echo $PATH'. 3. Aynı hatayı yaptınız, tekrar alt
John Freeman

41

Pistos'un cevabını temel alan bir çözüm sunuyorum, ancak kusurları yok.

  • Crontab'a şu satırı ekleyin, örneğin, crontab -e

    * * * * *  /usr/bin/env > /home/username/cron-env
    
  • Cron işleri ile aynı ortamda bir komutu çalıştıran bir kabuk betiği oluşturun:

    #!/bin/sh
    
    . "$1"
    exec /usr/bin/env -i "$SHELL" -c ". $1; $2"
    

kullanın:

run-as-cron <cron-environment> <command>

Örneğin

run-as-cron /home/username/cron-env 'echo $PATH'

İkinci argümanın bir argüman gerektirmesi durumunda alıntı yapılması gerektiğini unutmayın. Komut dosyasının ilk satırı tercüman olarak bir POSIX kabuğu yükler. İkinci satır cron ortam dosyasını besler. Bu, ortam değişkeninde depolanan doğru kabuğu yüklemek için gereklidir SHELL. Daha sonra boş bir ortam yüklüyor (ortam değişkenlerinin yeni kabuğa sızmasını önlemek için), cronjobs için kullanılan aynı kabuğu başlatıyor ve cron ortam değişkenlerini yüklüyor. Sonunda komut yürütülür.


Bu benim yakut ile ilgili Sfenks yükleme hatası çoğaltmak için bana yardımcı oldu.
13'te cweiske

1
Cron-env dosyasını yazmak için @reboot cron seçeneğini kullandım. Daha sonra crontab içinde bırakabilirsiniz ve sadece sistem başlatıldığında yeniden yazılacaktır. Satır eklemek / kaldırmak zorunda olmadığınızdan, işlemi biraz daha kolaylaştırır.
Michael Barton

Evet, Pistos çözümü benim için işe yaramadı ama bu yaptı
Stack

19

Crontab işi yapmazsa, içeriğini değiştirirsiniz:

crontab -l | grep -v '^#' | cut -f 6- -d ' ' | while read CMD; do eval $CMD; done

Bu ne yapar :

  • crontab işlerini listeler
  • yorum satırlarını kaldır
  • crontab yapılandırmasını kaldır
  • sonra onları tek tek başlat

5
Bu mutlaka, cron'un yapacağı ortamda aynı şekilde yapmıyor ve ben de onlardan sadece birini test etmek istediğini düşündüm.
Falcon Momot

2
doğru, yanılmışım ... Sadece işleri yönetiyor ama cron'un yapacağı gibi değil!
Django Janny

5
hala harika bir çözüm +1
Eric Uldall

1
Sadece sudo -H -u otheruser bash -c 'crontab..." başka bir kullanıcının crontab komutunu çalıştırabilirsiniz
Freedo

5

Varsayılan olarak, gördüğüm en varsayılan cron dramaları ile, şu anda burada cronun çalıştırılmasını söylemenin hiçbir yolu yoktur. Anakron kullanıyorsanız, ön planda ayrı bir örnek çalıştırmayı düşündüğüm mümkün olabilir.

Komut dosyalarınız düzgün çalışmıyorsa, bunu dikkate almazsınız.

  • komut dosyası belirli bir kullanıcı olarak çalışıyor
  • cron sınırlı bir çevreye sahiptir (bunun en belirgin tezahürü farklı bir yoldur).

Crontab'dan (5):

Çeşitli ortam değişkenleri cron (8) arka plan programı tarafından otomatik olarak kurulur. SHELL / bin / sh olarak ayarlandı ve LOGNAME ve HOME, crontab'ın sahibinin / etc / passwd satırından ayarlandı. PATH, "/ usr / bin: / bin" olarak ayarlanmış. HOME, SHELL ve PATH crontab'daki ayarlarla geçersiz kılınabilir; LOGNAME, işin çalıştığı kullanıcıdır ve değiştirilemeyebilir.

Genelde PATH en büyük sorundur, bu yüzden yapmanız gerekenler:

  • PATH komut dosyasını test ederken, açıkça / usr / bin: / bin olarak ayarlayın. Bunu bash dosyasında export PATH = "/ usr / bin: / bin" ile yapabilirsiniz.
  • Açıkça crontab'ın en üstünde istediğiniz PATH'i ayarlayın. örn. PATH = "/ usr / bin: / bin: / usr / yerel / bin: / usr / sbin: / sbin"

Komut dosyasını kabuksuz başka bir kullanıcı olarak çalıştırmanız gerekirse (örneğin, www-data), sudo kullanın:

sudo -u www-data /path/to/crontab-script.sh

Tüm bunlardan önce test edilecek ilk şey, elbette, komut dosyanızın aslında komut satırından ne yapması gerekiyorsa onu yapmasıdır. Komut satırından çalıştıramazsanız, açıkça cron ile çalışmayacak.


Tam cevap için teşekkür ederim. Belirli bir kullanıcı olarak ve belirli bir ortamla çalışmanın iki sorunun farkındayım. Böylece, şimdi göndereceğim kendi cevabımı
belirledim

Kaçış karakterleri, çalışmamak için geçerli bir nedendir
Joe Phillips

2

Marco'nun senaryosu bir sebepten dolayı işe yaramadı. Hata ayıklamak için zamanım olmadı, bu yüzden aynı şeyi yapan bir Python senaryosu yazdım. Daha uzun, ama: ilk önce benim için çalışıyor ve ikinci olarak da anlamayı daha kolay buluyorum. Ortamınızı kaydettiğiniz yere "/ tmp / cron-env" ifadesini değiştirin. İşte burada:

#!/usr/bin/env python
from __future__ import division, print_function

import sys
import os

def main():
    if len(sys.argv) != 2 or sys.argv[1] in ('-h', '--help'):
        print("Usage: {} CMD\n"
              "Run a command as cron would. Note that CMD must be quoted to be only one argument."
              .format(sys.argv[0]))
        sys.exit(1)
    _me, cmd = sys.argv
    env = dict(line.strip().split('=', 1) for line in open('/tmp/cron-env'))
    sh = env['SHELL']
    os.execvpe(sh, [sh, '-c', cmd], env)

if __name__ == '__main__':
    main()

1

Peki, kullanıcı crontab girişine koyduğunuzla aynı (veya crontab'ını dönüşümlü olarak koyduğunuz) aynı, bu yüzden bu hiç akıllıca değil. crontab(5) ayarlanan çevre değişkenlerinin bir listesini vermeli, sadece birkaçı var.


Başka bir deyişle, yapmanın bir yolu olmadığını mı söylüyorsunuz? Yalnızca "yeterince yakın" geçici çözümler mi var?
Pistos

Hayır, cevabımda verdiğim bilgileri kullanarak yapabileceğini söylüyorum.
womble

1

Örneğin vixie-cron gibi çoğu crontab'da değişkenleri crontab'a bu şekilde yerleştirebilir ve çalışıp çalışmadığını kontrol etmek için / usr / bin / env komutunu kullanabilirsiniz. Bu şekilde, cron olarak çalıştırılan komut dosyasında neyin yanlış olduğunu öğrendikten sonra komut dosyanızın crontab'da çalışmasını sağlayabilirsiniz.

SHELL=/bin/bash
LANG=en
FASEL=BLA

* * * * *   /usr/bin/env > /home/username/cron-env

1

Marco'nun çözümü benim için işe yaramadı ama Noam'ın python senaryosu işe yaradı. Marco'nun senaryosunun benim için çalışmasını sağlayan küçük bir değişiklik:

#!/bin/sh
. "$1"
exec /usr/bin/env -i "$SHELL" -c "set -a;. $1; $2"

Eklenen set -adışa aktarma değişkenleri $ 1 komut dosyasında tanımlandı ve $ 2 komutunda kullanılabilir duruma getirildi

ps Noam'ın pitonu, ortamı çocuk sürecine 'ihraç' ettiği için çalıştı.


1

Bu bir kabuk betiği ise, bu size en çok yol göstermelidir:

sudo su  # (assuming it's run as root, if not switch to the user you want it to run as)
cd  # Switch to home folder
sh <full-path/my-shell-script>

Her şey olmasa da kesinlikle bazı sorunları vurgulayacağım.


0

Cron işlerini elle çalıştırmanın bir yolunu bulamadım ama bu yazı cronjob ile aynı ortamı ayarlamayı ve komut dosyasını elle çalıştırmayı önerir.


OP'nin yapmayı bilmek istediği şeyi yapmayı önerdiğiniz şey değil mi?
womble

Bu yüzden nasıl yapılacağını açıklayan yazıya bağlantıyı dahil ettim. Buradaki her şeyi kopyala yapıştırmanın gerekli olduğunu düşünmedim.
oneodd1

0

Bir sonraki dakikaya başlamak için işi programlayabilirsiniz :)


7
59 saniye çok fazla zaman alıyor.
Stéphane Bruckert

OP, şu olasılığa değindi: "Bunu yapmanın bir yolu var mı? Senaryo değişikliklerini test etmek için 60 saniye beklemek zorunda değil."
Andrew Grimm

59 saniye muhtemelen önerilen diğer (ve garantili olmayan) çözümlerden herhangi birini seçmek ve uygulamaktan daha azdır. Böyle eksiklikleri gördüğümde Linux'un nasıl fiili standart bir sunucu işletim sistemi haline geldiğini merak ediyorum. Herhangi bir ciddi sysadmin işlerini test etmek istemez mi?
Rolf

0

Marco'nun cevabını dinledim. Kod aşağıda gösterilmiştir, ancak bu betiği burada tutacağım .

Bu crontab verildiğinde:

# m h  dom mon dow   command

X=Y
1 2 3 4 5 6 echo "Hello, world"
1 2 3 4 5 6 echo "Goodby, cruel world"
1 2 3 4 5 6 echo "Please spare me the drama"

Örnek kullanım oturumu:

$ cronTest
This is the crontab for  without comment lines or blank lines:
     1  X=Y
     2  echo "Hello, world"
     3  echo "Goodby, cruel world"
     4  echo "Please spare me the drama"
Which line would you like to run as  now?
55
55 is not valid, please enter an integer from 1 to 4
2

Evaluating 1: X=Y

Evaluating 2: echo "Hello, world"
Hello, world

Bu, cronTest2ortam değişkenlerini cron ile aynı şekilde ayarlamak için uygun şekilde çağrılması gerekiyor:

#!/bin/bash

# Prompt user for a user crontab entry to execute

function deleteTempFile {
  rm -f $TEMP_FILE
}

function debug {
  if [ "$DEBUG" ]; then >&2 printf "$1\n"; fi
}

function isValidLineNumber {
  # $1 - number of lines
  # $2 - requested line number
  if [[ -n "${2//[0-9]+/}" ]] && (( $2 <= $1 )); then echo true; else echo false; fi
}

function isVariableAssignment {
  [[ "$( echo "$1" | grep "=" )" ]]
}

function makeTempCrontab {
  local -r ASTERISK=\\*
  local -r NUMBER='[[:digit:]]{1,2}'
  local -r NUMBERS="$NUMBER(,$NUMBER)+"
  local -r CRON="^(($ASTERISK|$NUMBER|$NUMBERS)[[:space:]]+)"
  local -r CRON5_REGEX="$CRON{5}"
  local -r CRON6_REGEX="$CRON{6}"

  rm -f "$TEMP_FILE"

  local -r ALL_LINES="$( crontab -l )"

  # Ignore empty lines and lines starting with # (comment lines)
  local -r LINES="$( 
    echo "$ALL_LINES" | \
    grep -v '^[[:space:]]*#' | \
    grep -v '^[[:space:]]*$'
  )"

  if [[ -z "$LINES" ]]; then
    echo "Your crontab is empty, nothing to do"
    exit 1
  fi

  IFS=$'\n' 
  for LINE in $LINES; do
    LINE="$( echo "$LINE" | sed 's/\s\+$//e' )" # remove trailing space
    if [ "$( echo "$LINE" | grep "^$" )" ]; then  
      debug ""  # ignore empty line
    elif [ "$( echo "$LINE" | egrep "$CRON6_REGEX" )" ]; then
      debug "6 field date/time specifier: $LINE"
      # strip out when to run debug, leaving just the command to execute
      echo "$LINE" | cut -f 7- -d ' ' >> "$TEMP_FILE"
    elif [ "$( echo "$LINE" | egrep "$CRON5_REGEX" )" ]; then
      debug "5 field date/time specifier: $LINE"
      # strip out when to run debug, leaving just the command to execute
      echo "$LINE" | cut -f 6- -d ' ' >> "$TEMP_FILE"
    elif [ "$( echo "$LINE" | grep '^@' )" ]; then
      debug "@declaration: $LINE"
      # strip out @declaration, leaving just the command to execute
      echo "$LINE" | cut -f 2- -d ' ' >> "$TEMP_FILE"
    elif [ "$( echo "$LINE" | grep '=' )" ]; then
      debug "Variable assignment: $LINE"
      echo "$LINE"  >> "$TEMP_FILE"
    else
      debug "Ignored: $LINE"
    fi
  done
  unset IFS
}

function runUpToLine {
  # Scans up to given line number in $TEMP_FILE
  # Evaluates variable assignment
  # Executes specified line
  # Ignores remainder of file
  # Function definitions are not supported
  #
  # $1 - line number to run

  readarray CONTENTS < "$TEMP_FILE"
  for (( i=0; i<=$1; i++ )); do
    # >&2 echo "\$i=$i, \$1=$1, isVariableAssignment: $( isVariableAssignment $CONTENTS[$i] ), CONTENTS[$i]=${CONTENTS[$i]}"
    if isVariableAssignment ${CONTENTS[$i]} || (( $i == $1 )); then
      printf "\nEvaluating $(( i+1 )): ${CONTENTS[$i]}"
      eval "${CONTENTS[$i]}"
    fi
  done
}

function selectLine {
  >&2 echo "This is the crontab for $USER without comment lines or blank lines:"
  cat -n "$TEMP_FILE" >&2
  >&2 echo "Which line would you like to run as $USER now?"

  local -r NUM_LINES=$( cat "$TEMP_FILE" | wc -l )
  read LINE_NUMBER
  # >&2 echo "NUM_LINES=$NUM_LINES, LINE_NUMBER=$LINE_NUMBER;  valid: $( isValidLineNumber $NUM_LINES $LINE_NUMBER )"
  while [[ $( isValidLineNumber $NUM_LINES $LINE_NUMBER ) == false ]]; do
    >&2 echo "$LINE_NUMBER is not valid, please enter an integer from 1 to $NUM_LINES"
    read LINE_NUMBER
    # >&2 echo "NUM_LINES=$NUM_LINES, LINE_NUMBER=$LINE_NUMBER;  valid: $( isValidLineNumber $NUM_LINES $LINE_NUMBER )"
  done
  (( LINE_NUMBER-- ))
  echo ${LINE_NUMBER}
}

function doIt {
  export USER=$1
  local -r TEMP_FILE="$( mktemp crontabTest.XXX )"
  trap deleteTempFile EXIT

  makeTempCrontab
  local -r LINE_NUMBER="$( selectLine )"
  runUpToLine $LINE_NUMBER
}

doIt "$1" 

cronTestcronTest2ayarlanan uygun ortam değişkenleriyle çalışır :

#!/bin/bash

# Execute a user crontab entry with the proper environment

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

env -i bash --noprofile --norc -c "$DIR/cronTest2 $USER"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.