我想執行一個腳本並讓它每x分鐘運行一次命令。如何定期運行bash命令?
任何資源學習bash腳本的任何一般建議可能是非常酷。我使用Linux進行個人開發工作,所以bash腳本對我來說並不完全陌生,我只是沒有從頭開始編寫自己的腳本。
我想執行一個腳本並讓它每x分鐘運行一次命令。如何定期運行bash命令?
任何資源學習bash腳本的任何一般建議可能是非常酷。我使用Linux進行個人開發工作,所以bash腳本對我來說並不完全陌生,我只是沒有從頭開始編寫自己的腳本。
如果要定期運行的命令,有3種方式:
crontab
命令前。 while true; do ./my_script.sh; sleep 60; done
(不準確)使用systemd timer見cron
最佳的bash腳本的做法有些指針
http://mywiki.wooledge.org/BashFAQ
使用循環像* * * * * command
(運行每分鐘)
我要執行的腳本,並使其運行一個命令每{時間間隔}
cron
(https://en.wikipedia.org/wiki/Cron)就是爲此目的而設計的。如果您運行man cron
或man crontab
,您將找到有關如何使用它的說明。
有關學習bash腳本的任何資源的任何一般建議可能非常酷。我使用Linux進行個人開發工作,所以bash腳本對我來說並不完全陌生,我只是沒有從頭開始編寫自己的腳本。
如果您對bash的使用感覺很舒服,我建議您先閱讀bash的手冊頁(man bash
) - 這裏有很多很酷的小技巧。
MacOS的用戶:這裏是一個部分執行GNU watch
命令的(作爲0.3.0
版本)互動定期調用的主要視覺檢查:
這是語法兼容與GNU版本一起使用,如果使用未實現的特性,則會失敗並顯示特定的錯誤消息。
明顯侷限性:
--color
暗示)。還實現了一些非標準的特性,如等待成功(-E
),以補充等待錯誤(-e
)和顯示,以及最後調用的一天中的時間爲總時間的流逝至今。
運行watch -h
瞭解詳情。
例子:
watch -n 1 ls # list current dir every second
watch -e 'ls *.lockfile' # list lock files and exit once none exist anymore.
源代碼(粘貼到名爲watch
一個腳本文件,使其可執行文件,並將其放置在目錄中你$PATH
;注意語法這裏強調壞了,但代碼作品):
#!/usr/bin/env bash
THIS_NAME=$(basename "$BASH_SOURCE")
VERSION='0.1'
# Helper function for exiting with error message due to runtime error.
# die [errMsg [exitCode]]
# Default error message states context and indicates that execution is aborted. Default exit code is 1.
# Prefix for context is always prepended.
# Note: An error message is *always* printed; if you just want to exit with a specific code silently, use `exit n` directly.
die() {
echo "$THIS_NAME: ERROR: ${1:-"ABORTING due to unexpected error."}" 1>&2
exit ${2:-1} # Note: If the argument is non-numeric, the shell prints a warning and uses exit code 255.
}
# Helper function for exiting with error message due to invalid parameters.
# dieSyntax [errMsg]
# Default error message is provided, as is prefix and suffix; exit code is always 2.
dieSyntax() {
echo "$THIS_NAME: PARAMETER ERROR: ${1:-"Invalid parameter(s) specified."} Use -h for help." 1>&2
exit 2
}
# Get the elapsed time since the specified epoch time in format HH:MM:SS.
# Granularity: whole seconds.
# Example:
# tsStart=$(date +'%s')
# ...
# getElapsedTime $tsStart
getElapsedTime() {
date -j -u -f '%s' $(($(date +'%s') - $1)) +'%H:%M:%S'
}
# Command-line help.
if [[ "$1" == '--help' || "$1" == '-h' ]]; then
cat <<EOF
SYNOPSIS
$THIS_NAME [-n seconds] [opts] cmd [arg ...]
DESCRIPTION
Executes a command periodically and displays its output for visual inspection.
NOTE: This is a PARTIAL implementation of the GNU \`watch\` command, for OS X.
Notably, the output is not limited to one screenful, and displaying
output differences and using precise timing are not supported.
Also, colored output is always passed through (--color is implied).
Unimplemented features are marked as [NOT IMPLEMENTED] below.
Conversely, features specific to this implementation are marked as [NONSTD].
Reference version is GNU watch 0.3.0.
CMD may be a simple command with separately specified
arguments, if any, or a single string containing one or more
;-separated commands (including arguments) - in the former case the command
is directly executed by bash, in the latter the string is passed to \`bash -c\`.
Note that GNU watch uses sh, not bash.
To use \`exec\` instead, specify -x (see below).
By default, CMD is re-invoked indefinitely; terminate with ^-C or
exit based on conditions:
-e, --errexit
exits once CMD indicates an error, i.e., returns a non-zero exit code.
-E, --okexit [NONSTD]
is the inverse of -e: runs until CMD returns exit code 0.
By default, all output is passed through; the following options modify this
behavior; note that suppressing output only relates to CMD's output, not the
messages output by this utility itself:
-q, --quiet [NONSTD]
suppresses stdout output from the command invoked;
-Q, --quiet-both [NONSTD]
suppresses both stdout and stderr output.
-l, --list [NONSTD]
list-style display; i.e., suppresses clearing of the screen
before every invocation of CMD.
-n secs, --interval secs
interval in seconds between the end of the previous invocation of CMD
and the next invocation - 2 seconds by default, fractional values permitted;
thus, the interval between successive invocations is the specified interval
*plus* the last CMD's invocation's execution duration.
-x, --exec
uses \`exec\` rather than bash to execute CMD; this requires
arguments to be passed to CMD to be specified as separate arguments
to this utility and prevents any shell expansions of these arguments
at invocation time.
-t, --no-title
suppresses the default title (header) that displays the interval,
and (NONSTD) a time stamp, the time elapsed so far, and the command executed.
-b, --beep
beeps on error (bell signal), i.e., when CMD reports a non-zero exit code.
-c, --color
IMPLIED AND ALWAYS ON: colored command output is invariably passed through.
-p, --precise [NOT IMPLEMENTED]
-d, --difference [NOT IMPLEMENTED]
EXAMPLES
# List files in home folder every second.
$THIS_NAME -n 1 ls ~
# Wait until all *.lockfile files disappear from the current dir, checking every 2 secs.
$THIS_NAME -e 'ls *.lockfile'
EOF
exit 0
fi
# Make sure that we're running on OSX.
[[ $(uname) == 'Darwin' ]] || die "This script is designed to run on OS X only."
# Preprocess parameters: expand compressed options to individual options; e.g., '-ab' to '-a -b'
params=() decompressed=0 argsReached=0
for p in "[email protected]"; do
if [[ $argsReached -eq 0 && $p =~ ^-[a-zA-Z0-9]+$ ]]; then # compressed options?
decompressed=1
params+=(${p:0:2})
for ((i = 2; i < ${#p}; i++)); do
params+=("-${p:$i:1}")
done
else
((argsReached && ! decompressed)) && break
[[ $p == '--' || ${p:0:1} != '-' ]] && argsReached=1
params+=("$p")
fi
done
((decompressed)) && set -- "${params[@]}"; unset params decompressed argsReached p # Replace "[email protected]" with the expanded parameter set.
# Option-parameters loop.
interval=2 # default interval
runUntilFailure=0
runUntilSuccess=0
quietStdOut=0
quietStdOutAndStdErr=0
dontClear=0
noHeader=0
beepOnErr=0
useExec=0
while (($#)); do
case "$1" in
--) # Explicit end-of-options marker.
shift # Move to next param and proceed with data-parameter analysis below.
break
;;
-p|--precise|-d|--differences|--differences=*)
dieSyntax "Sadly, option $1 is NOT IMPLEMENTED."
;;
-v|--version)
echo "$VERSION"; exit 0
;;
-x|--exec)
useExec=1
;;
-c|--color)
# a no-op: unlike the GNU version, we always - and invariably - pass color codes through.
;;
-b|--beep)
beepOnErr=1
;;
-l|--list)
dontClear=1
;;
-e|--errexit)
runUntilFailure=1
;;
-E|--okexit)
runUntilSuccess=1
;;
-n|--interval)
shift; interval=$1;
errMsg="Please specify a positive number of seconds as the interval."
interval=$(bc <<<"$1") || dieSyntax "$errMsg"
((1 == $(bc <<<"$interval > 0"))) || dieSyntax "$errMsg"
[[ $interval == *.* ]] || interval+='.0'
;;
-t|--no-title)
noHeader=1
;;
-q|--quiet)
quietStdOut=1
;;
-Q|--quiet-both)
quietStdOutAndStdErr=1
;;
-?|--?*) # An unrecognized switch.
dieSyntax "Unrecognized option: '$1'. To force interpretation as non-option, precede with '--'."
;;
*) # 1st data parameter reached; proceed with *argument* analysis below.
break
;;
esac
shift
done
# Make sure we have at least a command name
[[ -n "$1" ]] || dieSyntax "Too few parameters specified."
# Suppress output streams, if requested.
# Duplicate stdout and stderr first.
# This allows us to produce output to stdout (>&3) and stderr (>&4) even when suppressed.
exec 3<&1 4<&2
if ((quietStdOutAndStdErr)); then
exec &> /dev/null
elif ((quietStdOut)); then
exec 1> /dev/null
fi
# Set an exit trap to ensure that the duplicated file descriptors are closed.
trap 'exec 3>&- 4>&-' EXIT
# Start loop with periodic invocation.
# Note: We use `eval` so that compound commands - e.g. 'ls; bash --version' - can be passed.
tsStart=$(date +'%s')
while :; do
((dontClear)) || clear
((noHeader)) || echo "Every ${interval}s. [$(date +'%H:%M:%S') - elapsed: $(getElapsedTime $tsStart)]: [email protected]"$'\n' >&3
if ((useExec)); then
(exec "[email protected]") # run in *subshell*, otherwise *this* script will be replaced by the process invoked
else
if [[ $* == *' '* ]]; then
# A single argument with interior spaces was provided -> we must use `bash -c` to evaluate it properly.
bash -c "$*"
else
# A command name only or a command name + arguments were specified as separate arguments -> let bash run it directly.
"[email protected]"
fi
fi
ec=$?
((ec != 0 && beepOnErr)) && printf '\a'
((ec == 0 && runUntilSuccess)) && { echo $'\n'"[$(date +'%H:%M:%S') - elapsed: $(getElapsedTime $tsStart)] Exiting as requested: exit code 0 reported." >&3; exit 0; }
((ec != 0 && runUntilFailure)) && { echo $'\n'"[$(date +'%H:%M:%S') - elapsed: $(getElapsedTime $tsStart)] Exiting as requested: non-zero exit code ($ec) reported." >&3; exit 0; }
sleep $interval
done
你看看'cron'嗎? – SheetJS
至於你對資源的要求,bash-hacker維基的資源清單(http://wiki.bash-hackers.org/scripting/tutoriallist)有一個精心修剪,以避免指南不要小心避免經典的[陷阱](http://mywiki.wooledge.org/BashPitfalls)。 – kojiro